@canmingir/link 1.2.23 → 1.2.25
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/package.json +3 -1
- package/src/Platform.jsx +10 -14
- package/src/context/Context.js +98 -0
- package/src/context/reducer.js +590 -10
- package/src/layouts/auth/modern.jsx +2 -2
- package/src/lib/APIDialogAction/APIDialogAction.jsx +109 -0
- package/src/lib/APIDialogAction/index.js +1 -0
- package/src/lib/APIDialogAction/styles.js +6 -0
- package/src/lib/APIParams/APIParams.jsx +57 -0
- package/src/lib/APIParams/index.js +1 -0
- package/src/lib/APIPath/APIPath.jsx +82 -0
- package/src/lib/APIPath/index.js +1 -0
- package/src/lib/APIPath/styles.js +19 -0
- package/src/lib/APITree/APITree.jsx +409 -0
- package/src/lib/APITree/Arrow.jsx +21 -0
- package/src/lib/APITree/DeleteMethodDialog.jsx +41 -0
- package/src/lib/APITree/index.js +1 -0
- package/src/lib/APITree/styles.js +19 -0
- package/src/lib/APITypes/APITypes.jsx +141 -0
- package/src/lib/APITypes/TypeEditor.jsx +46 -0
- package/src/lib/APITypes/TypeList.jsx +180 -0
- package/src/lib/APITypes/index.js +1 -0
- package/src/lib/BlankTreeMessage/BlankTreeMessage.jsx +39 -0
- package/src/lib/BlankTreeMessage/index.js +1 -0
- package/src/lib/DialogTootip/DialogTooltip.jsx +67 -0
- package/src/lib/DialogTootip/index.js +1 -0
- package/src/lib/DialogTootip/styles.js +9 -0
- package/src/lib/NewApiBody/NewAPIBody.jsx +97 -0
- package/src/lib/NewApiBody/ParamView.jsx +38 -0
- package/src/lib/NucDialog/NucDialog.jsx +108 -0
- package/src/lib/NucDialog/index.js +1 -0
- package/src/lib/ParamTable/ParamTable.jsx +133 -0
- package/src/lib/ParamTable/TypeMenu.jsx +102 -0
- package/src/lib/ParamTable/defaults.js +47 -0
- package/src/lib/ParamTable/index.js +1 -0
- package/src/lib/ParamTable/styles.js +12 -0
- package/src/lib/ResourceMenu/AlertMassage.jsx +28 -0
- package/src/lib/ResourceMenu/DeleteResourceDialog.jsx +60 -0
- package/src/lib/ResourceMenu/ResourceMenu.jsx +156 -0
- package/src/lib/ResourceMenu/index.js +1 -0
- package/src/lib/ResourceMenu/styles.js +5 -0
- package/src/lib/Schema/Schema.jsx +204 -0
- package/src/lib/Schema/index.js +1 -0
- package/src/lib/SchemaEditor/SchemaEditor.jsx +258 -0
- package/src/lib/SchemaEditor/SchemaEditor.test.js +193 -0
- package/src/lib/SchemaEditor/SchemaPropertyEditor.jsx +135 -0
- package/src/lib/SchemaEditor/SchemaUtils.js +152 -0
- package/src/lib/SchemaEditor/index.js +1 -0
- package/src/lib/ToggleableMenu/ToggleableMenu.jsx +35 -0
- package/src/lib/ToggleableMenu/index.js +1 -0
- package/src/lib/index.js +14 -0
- package/src/pages/Callback.jsx +2 -4
- package/src/pages/LoginPage.jsx +3 -12
- package/src/stories/APITree.stories.jsx +429 -0
- package/src/stories/FlowChart.stories.jsx +1 -1
- package/src/templates/ActionTemplate.js +24 -0
- package/src/widgets/Login/CognitoLogin.jsx +2 -2
- package/src/widgets/Login/DemoLogin.jsx +1 -1
- package/src/widgets/LoginForm/LoginForm.jsx +8 -3
- package/src/widgets/SettingsDialog.jsx +33 -11
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
|
2
|
+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
3
|
+
import { v4 as uuidv4 } from "uuid";
|
|
4
|
+
|
|
5
|
+
import { Box, Typography } from "@mui/material";
|
|
6
|
+
import React, { useEffect, useState } from "react";
|
|
7
|
+
import { SimpleTreeView } from "@mui/x-tree-view/SimpleTreeView";
|
|
8
|
+
import { TreeItem } from "@mui/x-tree-view/TreeItem";
|
|
9
|
+
|
|
10
|
+
const Schema = ({ initialData = {}, customTypes = [] }) => {
|
|
11
|
+
const [schemaData, setSchemaData] = useState({});
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const addIdsToSchema = (schema) => {
|
|
15
|
+
if (!schema) return null;
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
...schema,
|
|
19
|
+
id: uuidv4(),
|
|
20
|
+
properties: schema.properties?.map(addIdsToSchema),
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (Object.keys(initialData).length === 0) {
|
|
25
|
+
setSchemaData({
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: [],
|
|
28
|
+
});
|
|
29
|
+
} else {
|
|
30
|
+
setSchemaData(addIdsToSchema(initialData));
|
|
31
|
+
}
|
|
32
|
+
}, [initialData]);
|
|
33
|
+
|
|
34
|
+
const renderTree = (node, level = 0) => (
|
|
35
|
+
<TreeItem
|
|
36
|
+
key={node.id}
|
|
37
|
+
itemId={level === 0 ? "1" : node.id}
|
|
38
|
+
label={
|
|
39
|
+
<div
|
|
40
|
+
style={{
|
|
41
|
+
display: "flex",
|
|
42
|
+
alignItems: "center",
|
|
43
|
+
justifyContent: "space-between",
|
|
44
|
+
width: "100%",
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
<div
|
|
48
|
+
style={{
|
|
49
|
+
display: "flex",
|
|
50
|
+
alignItems: "center",
|
|
51
|
+
gap: "1px",
|
|
52
|
+
width: "100%",
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
<Box
|
|
56
|
+
sx={{
|
|
57
|
+
display: "flex",
|
|
58
|
+
justifyContent: "space-between",
|
|
59
|
+
width: "100%",
|
|
60
|
+
gap: "4px",
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
<Box
|
|
64
|
+
sx={{
|
|
65
|
+
display: "flex",
|
|
66
|
+
alignItems: "center",
|
|
67
|
+
cursor: "pointer",
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
<Typography
|
|
71
|
+
variant="body2"
|
|
72
|
+
sx={{
|
|
73
|
+
padding: "2px 2px",
|
|
74
|
+
borderRadius: "4px",
|
|
75
|
+
}}
|
|
76
|
+
>
|
|
77
|
+
{level === 0 ? "" : node.name}
|
|
78
|
+
</Typography>
|
|
79
|
+
</Box>
|
|
80
|
+
|
|
81
|
+
<Box
|
|
82
|
+
sx={{
|
|
83
|
+
display: "flex",
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
cursor: "pointer",
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
<Typography
|
|
89
|
+
variant="body2"
|
|
90
|
+
sx={{
|
|
91
|
+
cursor: "pointer",
|
|
92
|
+
borderRadius: "4px",
|
|
93
|
+
}}
|
|
94
|
+
>
|
|
95
|
+
{node.type}
|
|
96
|
+
</Typography>
|
|
97
|
+
</Box>
|
|
98
|
+
</Box>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
}
|
|
102
|
+
>
|
|
103
|
+
{Array.isArray(node.properties)
|
|
104
|
+
? node.properties.map((childNode) => renderTree(childNode, level + 1))
|
|
105
|
+
: isCustomType(node.type)
|
|
106
|
+
? renderCustomTypeNode(node)
|
|
107
|
+
: null}
|
|
108
|
+
</TreeItem>
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const isCustomType = (type) => {
|
|
112
|
+
return customTypes.some((customType) => customType.name === type);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const renderCustomTypeNode = (node) => {
|
|
116
|
+
const customTypeSchema = customTypes.find(
|
|
117
|
+
(type) => type.name === node.type
|
|
118
|
+
)?.schema;
|
|
119
|
+
|
|
120
|
+
if (!customTypeSchema || !customTypeSchema.properties) {
|
|
121
|
+
return <Box sx={{ paddingLeft: "20px" }}>No properties defined</Box>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Box sx={{ paddingLeft: "20px" }}>
|
|
126
|
+
{customTypeSchema.properties.map((prop, index) => (
|
|
127
|
+
<Box
|
|
128
|
+
key={index}
|
|
129
|
+
sx={{
|
|
130
|
+
paddingTop: "5px",
|
|
131
|
+
paddingBottom: "5px",
|
|
132
|
+
display: "flex",
|
|
133
|
+
alignItems: "center",
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
<Typography
|
|
137
|
+
variant="body2"
|
|
138
|
+
sx={{
|
|
139
|
+
color: (theme) => theme.palette.grey[600],
|
|
140
|
+
}}
|
|
141
|
+
>
|
|
142
|
+
{prop.name}
|
|
143
|
+
</Typography>
|
|
144
|
+
<Typography
|
|
145
|
+
variant="body2"
|
|
146
|
+
sx={{
|
|
147
|
+
marginLeft: "8px",
|
|
148
|
+
color: (theme) => theme.palette.grey[500],
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{prop.type}
|
|
152
|
+
</Typography>
|
|
153
|
+
</Box>
|
|
154
|
+
))}
|
|
155
|
+
</Box>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<SimpleTreeView
|
|
161
|
+
slots={{
|
|
162
|
+
collapseIcon: () => <ExpandMoreIcon />,
|
|
163
|
+
expandIcon: () => <ChevronRightIcon />,
|
|
164
|
+
}}
|
|
165
|
+
defaultExpandedItems={["1"]}
|
|
166
|
+
sx={{
|
|
167
|
+
flexGrow: 1,
|
|
168
|
+
overflowY: "auto",
|
|
169
|
+
width: "100%",
|
|
170
|
+
".MuiTreeItem-root": {
|
|
171
|
+
alignItems: "center",
|
|
172
|
+
},
|
|
173
|
+
".MuiTreeItem-content": {
|
|
174
|
+
width: "100%",
|
|
175
|
+
display: "flex",
|
|
176
|
+
justifyContent: "space-between",
|
|
177
|
+
padding: "1px 8px",
|
|
178
|
+
borderRadius: "4px",
|
|
179
|
+
margin: "1px 0",
|
|
180
|
+
transition: "width 0.3s ease-in-out, height 0.3s ease-in-out",
|
|
181
|
+
},
|
|
182
|
+
".MuiTreeItem-label": {
|
|
183
|
+
width: "100%",
|
|
184
|
+
fontWeight: "bold",
|
|
185
|
+
},
|
|
186
|
+
".MuiTreeItem-group": {
|
|
187
|
+
marginLeft: "16px !important",
|
|
188
|
+
paddingLeft: "8px",
|
|
189
|
+
borderLeft: `1px solid`,
|
|
190
|
+
borderColor: (theme) => theme.palette.grey[400],
|
|
191
|
+
},
|
|
192
|
+
".MuiTreeItem-iconContainer": {
|
|
193
|
+
minWidth: "0",
|
|
194
|
+
marginRight: "0px",
|
|
195
|
+
padding: "0px",
|
|
196
|
+
},
|
|
197
|
+
}}
|
|
198
|
+
>
|
|
199
|
+
{renderTree(schemaData, 0)}
|
|
200
|
+
</SimpleTreeView>
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default Schema;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./Schema";
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
|
|
2
|
+
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
|
|
3
|
+
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
|
4
|
+
import IconButton from "@mui/material/IconButton";
|
|
5
|
+
import RemoveCircleOutlineIcon from "@mui/icons-material/RemoveCircleOutline";
|
|
6
|
+
import SchemaPropertyEditor from "./SchemaPropertyEditor";
|
|
7
|
+
import { v4 as uuidv4 } from "uuid";
|
|
8
|
+
|
|
9
|
+
import { Box, Typography } from "@mui/material";
|
|
10
|
+
import React, { forwardRef, useEffect, useState } from "react";
|
|
11
|
+
import { SimpleTreeView } from "@mui/x-tree-view/SimpleTreeView";
|
|
12
|
+
import { TreeItem } from "@mui/x-tree-view/TreeItem";
|
|
13
|
+
import { addProperty, changeProperty, removeProperty } from "./SchemaUtils";
|
|
14
|
+
|
|
15
|
+
const SchemaEditor = forwardRef(
|
|
16
|
+
({ initialData = {}, customTypes = [] }, ref) => {
|
|
17
|
+
const [schemaData, setSchemaData] = useState({});
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const addIdsToSchema = (schema) => {
|
|
21
|
+
return {
|
|
22
|
+
...schema,
|
|
23
|
+
id: uuidv4(),
|
|
24
|
+
properties: schema.properties?.map(addIdsToSchema),
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
if (!schemaData || Object.keys(schemaData).length === 0) {
|
|
29
|
+
if (Object.keys(initialData).length === 0) {
|
|
30
|
+
setSchemaData({
|
|
31
|
+
type: "object",
|
|
32
|
+
properties: [],
|
|
33
|
+
});
|
|
34
|
+
} else {
|
|
35
|
+
const dataWithIds = addIdsToSchema(initialData);
|
|
36
|
+
setSchemaData(dataWithIds);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}, [initialData, schemaData]);
|
|
40
|
+
|
|
41
|
+
const handleAddProperty = (newProperty, parentId = null) => {
|
|
42
|
+
addProperty(parentId, setSchemaData);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleRemoveProperty = (propertyId) => {
|
|
46
|
+
removeProperty(propertyId, setSchemaData);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const handleChangeProperty = (propertyId, changes) => {
|
|
50
|
+
changeProperty(propertyId, changes, setSchemaData);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const renderTree = (node, level = 0) => (
|
|
54
|
+
<TreeItem
|
|
55
|
+
key={node.id}
|
|
56
|
+
itemId={level === 0 ? "1" : node.id}
|
|
57
|
+
label={
|
|
58
|
+
<Box
|
|
59
|
+
sx={{
|
|
60
|
+
display: "flex",
|
|
61
|
+
alignItems: "center",
|
|
62
|
+
justifyContent: "space-between",
|
|
63
|
+
width: "100%",
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<Box
|
|
67
|
+
sx={{
|
|
68
|
+
display: "flex",
|
|
69
|
+
alignItems: "center",
|
|
70
|
+
gap: "1px",
|
|
71
|
+
width: "100%",
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<SchemaPropertyEditor
|
|
75
|
+
node={{ ...node, level }}
|
|
76
|
+
disableNameChange={level === 0}
|
|
77
|
+
onNameChange={(newName) => {
|
|
78
|
+
handleChangeProperty(node.id, {
|
|
79
|
+
name: newName,
|
|
80
|
+
type: node.type,
|
|
81
|
+
});
|
|
82
|
+
}}
|
|
83
|
+
onTypeChange={(newType) => {
|
|
84
|
+
handleChangeProperty(level === 0 ? "1" : node.id, {
|
|
85
|
+
name: node.name,
|
|
86
|
+
type: newType,
|
|
87
|
+
});
|
|
88
|
+
}}
|
|
89
|
+
customTypes={customTypes}
|
|
90
|
+
/>
|
|
91
|
+
{true && (
|
|
92
|
+
<IconButton
|
|
93
|
+
size="small"
|
|
94
|
+
onClick={(e) => {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
e.stopPropagation();
|
|
97
|
+
handleAddProperty(
|
|
98
|
+
{ type: "string", name: "newProperty" },
|
|
99
|
+
level === 0 ? null : node.id
|
|
100
|
+
);
|
|
101
|
+
}}
|
|
102
|
+
disabled={
|
|
103
|
+
node.type !== "object" ||
|
|
104
|
+
(node.type === "array" && node.properties.length >= 1)
|
|
105
|
+
}
|
|
106
|
+
sx={{
|
|
107
|
+
color: (theme) => theme.palette.grey[600],
|
|
108
|
+
marginRight: "-8px ",
|
|
109
|
+
}}
|
|
110
|
+
data-cy={`add-property-button-${node.id}`}
|
|
111
|
+
>
|
|
112
|
+
<AddCircleOutlineIcon fontSize="small" />
|
|
113
|
+
</IconButton>
|
|
114
|
+
)}
|
|
115
|
+
</Box>
|
|
116
|
+
|
|
117
|
+
{true && (
|
|
118
|
+
<IconButton
|
|
119
|
+
size="small"
|
|
120
|
+
style={{ marginLeft: "auto" }}
|
|
121
|
+
onClick={(e) => {
|
|
122
|
+
e.preventDefault();
|
|
123
|
+
e.stopPropagation();
|
|
124
|
+
handleRemoveProperty(node.id);
|
|
125
|
+
}}
|
|
126
|
+
disabled={level === 0}
|
|
127
|
+
sx={{
|
|
128
|
+
color: (theme) => theme.palette.grey[600],
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<RemoveCircleOutlineIcon fontSize="small" />
|
|
132
|
+
</IconButton>
|
|
133
|
+
)}
|
|
134
|
+
</Box>
|
|
135
|
+
}
|
|
136
|
+
>
|
|
137
|
+
{Array.isArray(node.properties)
|
|
138
|
+
? node.properties.map((childNode) => renderTree(childNode, level + 1))
|
|
139
|
+
: isCustomType(node.type)
|
|
140
|
+
? renderCustomTypeNode(node)
|
|
141
|
+
: null}
|
|
142
|
+
</TreeItem>
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const isCustomType = (type) => {
|
|
146
|
+
return customTypes.some((customType) => customType.name === type);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const renderCustomTypeNode = (node) => {
|
|
150
|
+
const customTypeSchema = customTypes.find(
|
|
151
|
+
(type) => type.name === node.type
|
|
152
|
+
)?.schema;
|
|
153
|
+
|
|
154
|
+
if (!customTypeSchema || !customTypeSchema.properties) {
|
|
155
|
+
return <Box sx={{ paddingLeft: "20px" }}>No properties defined</Box>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<Box sx={{ paddingLeft: "20px" }}>
|
|
160
|
+
{customTypeSchema.properties.map((prop, index) => (
|
|
161
|
+
<Box
|
|
162
|
+
key={index}
|
|
163
|
+
sx={{
|
|
164
|
+
paddingTop: "5px",
|
|
165
|
+
paddingBottom: "5px",
|
|
166
|
+
display: "flex",
|
|
167
|
+
alignItems: "center",
|
|
168
|
+
}}
|
|
169
|
+
>
|
|
170
|
+
<Typography
|
|
171
|
+
variant="body2"
|
|
172
|
+
sx={{
|
|
173
|
+
color: (theme) => theme.palette.grey[600],
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
{prop.name}
|
|
177
|
+
</Typography>
|
|
178
|
+
<Typography
|
|
179
|
+
variant="body2"
|
|
180
|
+
sx={{
|
|
181
|
+
marginLeft: "8px",
|
|
182
|
+
color: (theme) => theme.palette.grey[500],
|
|
183
|
+
}}
|
|
184
|
+
>
|
|
185
|
+
{prop.type}
|
|
186
|
+
</Typography>
|
|
187
|
+
</Box>
|
|
188
|
+
))}
|
|
189
|
+
</Box>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const schemaOutput = () => {
|
|
194
|
+
const removeIds = (node) => {
|
|
195
|
+
// eslint-disable-next-line no-unused-vars
|
|
196
|
+
const { id, properties, ...rest } = node;
|
|
197
|
+
if ((node.type === "object" || node.type === "array") && properties) {
|
|
198
|
+
return { ...rest, properties: properties.map(removeIds) };
|
|
199
|
+
}
|
|
200
|
+
return rest;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return removeIds(schemaData);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
React.useImperativeHandle(ref, () => ({
|
|
207
|
+
schemaOutput: schemaOutput,
|
|
208
|
+
}));
|
|
209
|
+
|
|
210
|
+
const schemaOutputWithIDs = () => schemaData;
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<SimpleTreeView
|
|
214
|
+
slots={{
|
|
215
|
+
collapseIcon: () => <ExpandMoreIcon data-cy="collapse-icon" />,
|
|
216
|
+
expandIcon: () => <ChevronRightIcon data-cy="expand-icon" />,
|
|
217
|
+
}}
|
|
218
|
+
defaultExpandedItems={["1"]}
|
|
219
|
+
sx={{
|
|
220
|
+
flexGrow: 1,
|
|
221
|
+
overflowY: "auto",
|
|
222
|
+
width: "100%",
|
|
223
|
+
".MuiTreeItem-root": {
|
|
224
|
+
alignItems: "center",
|
|
225
|
+
},
|
|
226
|
+
".MuiTreeItem-content": {
|
|
227
|
+
width: "100%",
|
|
228
|
+
display: "flex",
|
|
229
|
+
justifyContent: "space-between",
|
|
230
|
+
padding: "1px 8px",
|
|
231
|
+
borderRadius: "4px",
|
|
232
|
+
margin: "1px 0",
|
|
233
|
+
transition: "width 0.3s ease-in-out, height 0.3s ease-in-out",
|
|
234
|
+
},
|
|
235
|
+
".MuiTreeItem-label": {
|
|
236
|
+
width: "100%",
|
|
237
|
+
fontWeight: "bold",
|
|
238
|
+
},
|
|
239
|
+
".MuiTreeItem-group": {
|
|
240
|
+
marginLeft: "16px !important",
|
|
241
|
+
paddingLeft: "8px",
|
|
242
|
+
borderLeft: `1px solid`,
|
|
243
|
+
borderColor: (theme) => theme.palette.grey[400],
|
|
244
|
+
},
|
|
245
|
+
".MuiTreeItem-iconContainer": {
|
|
246
|
+
minWidth: "0",
|
|
247
|
+
marginRight: "0px",
|
|
248
|
+
padding: "0px",
|
|
249
|
+
},
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{renderTree(schemaData, 0)}
|
|
253
|
+
</SimpleTreeView>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
export default SchemaEditor;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import SchemaEditor from "./SchemaEditor";
|
|
5
|
+
import { act } from "react-dom/test-utils";
|
|
6
|
+
|
|
7
|
+
import { render, screen } from "@testing-library/react";
|
|
8
|
+
|
|
9
|
+
describe("SchemaEditor Component", () => {
|
|
10
|
+
const initialSchema = {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: [
|
|
13
|
+
{ type: "string", name: "initial" },
|
|
14
|
+
{ type: "integer", name: "schema" },
|
|
15
|
+
{
|
|
16
|
+
type: "object",
|
|
17
|
+
name: "object",
|
|
18
|
+
properties: [{ type: "integer", name: "nested" }],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
test("renders with initial schema", () => {
|
|
24
|
+
console.log("rendering ");
|
|
25
|
+
render(<SchemaEditor initialData={initialSchema} />);
|
|
26
|
+
|
|
27
|
+
expect(screen.getByDisplayValue(/initial/)).toBeInTheDocument();
|
|
28
|
+
expect(screen.getByDisplayValue(/schema/)).toBeInTheDocument();
|
|
29
|
+
|
|
30
|
+
// expect(screen.getByText(/nested/)).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("initializes without initial data", () => {
|
|
34
|
+
console.log("initializing without initial data");
|
|
35
|
+
render(<SchemaEditor />);
|
|
36
|
+
|
|
37
|
+
const expectedSchema = {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: [],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const outputSchema = SchemaEditor.schemaOutput();
|
|
43
|
+
expect(outputSchema).toEqual(expectedSchema);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("provides output correctly with initial data", () => {
|
|
47
|
+
console.log("provides output correctly with initial data");
|
|
48
|
+
render(<SchemaEditor initialData={initialSchema} />);
|
|
49
|
+
|
|
50
|
+
const outputSchema = SchemaEditor.schemaOutput();
|
|
51
|
+
expect(outputSchema).toEqual(initialSchema);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("adds a new property", () => {
|
|
55
|
+
console.log("adds a new property");
|
|
56
|
+
const emptySchema = { type: "object", properties: [] };
|
|
57
|
+
render(<SchemaEditor initialData={emptySchema} />);
|
|
58
|
+
|
|
59
|
+
act(() => {
|
|
60
|
+
SchemaEditor.addProperty(null);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
let updatedSchema = SchemaEditor.schemaOutput();
|
|
64
|
+
expect(updatedSchema.properties).toHaveLength(1);
|
|
65
|
+
expect(updatedSchema.properties[0].name).toBe("id");
|
|
66
|
+
|
|
67
|
+
act(() => {
|
|
68
|
+
SchemaEditor.addProperty(null);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
updatedSchema = SchemaEditor.schemaOutput();
|
|
72
|
+
expect(updatedSchema.properties).toHaveLength(2);
|
|
73
|
+
expect(updatedSchema.properties[1].name).toBe("prop2");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("removes a property by ID", () => {
|
|
77
|
+
console.log("removes a property by ID");
|
|
78
|
+
render(<SchemaEditor initialData={initialSchema} />);
|
|
79
|
+
|
|
80
|
+
const currentSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
81
|
+
|
|
82
|
+
const propertyToRemove = currentSchemaWithIDs.properties.find(
|
|
83
|
+
(prop) => prop.name === "initial"
|
|
84
|
+
);
|
|
85
|
+
if (!propertyToRemove) {
|
|
86
|
+
throw new Error("Property to remove not found");
|
|
87
|
+
}
|
|
88
|
+
act(() => {
|
|
89
|
+
SchemaEditor.removeProperty(propertyToRemove.id);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const newCurrentSchema = SchemaEditor.schemaOutput();
|
|
93
|
+
const isPropertyRemoved = newCurrentSchema.properties.every(
|
|
94
|
+
(prop) => prop.name !== "initial"
|
|
95
|
+
);
|
|
96
|
+
expect(isPropertyRemoved).toBeTruthy();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("changes a property type and name", () => {
|
|
100
|
+
console.log("changes a property type and name");
|
|
101
|
+
render(<SchemaEditor initialData={initialSchema} />);
|
|
102
|
+
|
|
103
|
+
const currentSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
104
|
+
|
|
105
|
+
const propertyToChange = currentSchemaWithIDs.properties.find(
|
|
106
|
+
(prop) => prop.name === "initial"
|
|
107
|
+
);
|
|
108
|
+
if (!propertyToChange) {
|
|
109
|
+
throw new Error("Property to change not found");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const change = {
|
|
113
|
+
name: "initial2",
|
|
114
|
+
type: "integer",
|
|
115
|
+
};
|
|
116
|
+
act(() => {
|
|
117
|
+
SchemaEditor.changeProperty(propertyToChange.id, change);
|
|
118
|
+
});
|
|
119
|
+
const updatedSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
120
|
+
|
|
121
|
+
const changedProperty = updatedSchemaWithIDs.properties.find(
|
|
122
|
+
(prop) => prop.id === propertyToChange.id
|
|
123
|
+
);
|
|
124
|
+
expect(changedProperty).toBeDefined();
|
|
125
|
+
expect(changedProperty.name).toBe(change.name);
|
|
126
|
+
expect(changedProperty.type).toBe(change.type);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("adds a nested object and a property to it", () => {
|
|
130
|
+
console.log("adds a nested object and a property to it");
|
|
131
|
+
render(<SchemaEditor initialData={initialSchema} />);
|
|
132
|
+
|
|
133
|
+
act(() => {
|
|
134
|
+
SchemaEditor.addProperty(null);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
let updatedSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
138
|
+
const newNestedObjectId = updatedSchemaWithIDs.properties.find(
|
|
139
|
+
(prop) => prop.name === "prop4"
|
|
140
|
+
).id;
|
|
141
|
+
|
|
142
|
+
act(() => {
|
|
143
|
+
SchemaEditor.addProperty(newNestedObjectId);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
updatedSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
147
|
+
const updatedNestedObject = updatedSchemaWithIDs.properties.find(
|
|
148
|
+
(prop) => prop.id === newNestedObjectId
|
|
149
|
+
);
|
|
150
|
+
console.log("updatedNestedObject: ", updatedNestedObject);
|
|
151
|
+
expect(updatedNestedObject).toBeDefined();
|
|
152
|
+
expect(updatedNestedObject.name).toBe("prop4");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("changes a property to custom type", () => {
|
|
156
|
+
console.log("changes a property to custom type");
|
|
157
|
+
const customTypes = [
|
|
158
|
+
{
|
|
159
|
+
name: "Item",
|
|
160
|
+
schema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: [
|
|
163
|
+
{ type: "string", name: "id" },
|
|
164
|
+
{ type: "string", name: "name" },
|
|
165
|
+
{ type: "string", name: "barcode" },
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
render(
|
|
172
|
+
<SchemaEditor initialData={initialSchema} customTypes={customTypes} />
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
const currentSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
176
|
+
const propertyToChange = currentSchemaWithIDs.properties[0];
|
|
177
|
+
|
|
178
|
+
const change = {
|
|
179
|
+
type: "Item",
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
act(() => {
|
|
183
|
+
SchemaEditor.changeProperty(propertyToChange.id, change);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const updatedSchemaWithIDs = SchemaEditor.schemaOutputWithIDs();
|
|
187
|
+
const updatedProperty = updatedSchemaWithIDs.properties.find(
|
|
188
|
+
(prop) => prop.id === propertyToChange.id
|
|
189
|
+
);
|
|
190
|
+
expect(updatedProperty).toBeDefined();
|
|
191
|
+
expect(updatedProperty.type).toBe("Item");
|
|
192
|
+
});
|
|
193
|
+
});
|