@truedat/dd 5.3.0 → 5.3.2
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/CHANGELOG.md +12 -0
- package/package.json +6 -6
- package/src/api/queries.js +11 -0
- package/src/api.js +5 -0
- package/src/components/LinkLabelsSelector.js +74 -0
- package/src/components/StructureStructureForm.js +95 -0
- package/src/components/StructureStructureLinks.js +89 -3
- package/src/components/StructureTabPane.js +11 -1
- package/src/components/StructureTabPaneRoutes.js +2 -2
- package/src/components/StructureTabRoutes.js +2 -2
- package/src/components/StructureTabs.js +1 -1
- package/src/components/__tests__/StructureStructureForm.spec.js +138 -0
- package/src/components/__tests__/StructureStructureLinks.spec.js +109 -35
- package/src/messages/en.js +2 -0
- package/src/messages/es.js +3 -0
- package/src/reducers/structureRedirect.js +6 -0
- package/src/routines.js +3 -1
- package/src/sagas/__tests__/deleteStructureStructureLink.spec.js +101 -0
- package/src/sagas/deleteStructureStructureLink.js +45 -0
- package/src/sagas/index.js +6 -0
- package/src/sagas/linkStructureToStructure.js +46 -0
- package/src/selectors/__tests__/getStructuresFields.spec.js +2 -2
- package/src/selectors/getActiveTab.js +2 -2
- package/src/selectors/getTabVisibility.js +1 -0
- package/src/components/__tests__/__snapshots__/StructureStructureLinks.spec.js.snap +0 -287
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.2",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"jsnext:main": "src/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@testing-library/jest-dom": "^5.16.5",
|
|
35
35
|
"@testing-library/react": "^12.0.0",
|
|
36
36
|
"@testing-library/user-event": "^13.2.1",
|
|
37
|
-
"@truedat/test": "5.2
|
|
37
|
+
"@truedat/test": "5.3.2",
|
|
38
38
|
"babel-jest": "^28.1.0",
|
|
39
39
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
40
40
|
"babel-plugin-lodash": "^3.3.4",
|
|
@@ -87,9 +87,9 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@apollo/client": "^3.7.1",
|
|
90
|
-
"@truedat/auth": "5.3.
|
|
91
|
-
"@truedat/core": "5.3.
|
|
92
|
-
"@truedat/df": "5.3.
|
|
90
|
+
"@truedat/auth": "5.3.2",
|
|
91
|
+
"@truedat/core": "5.3.2",
|
|
92
|
+
"@truedat/df": "5.3.2",
|
|
93
93
|
"lodash": "^4.17.21",
|
|
94
94
|
"moment": "^2.29.4",
|
|
95
95
|
"path-to-regexp": "^1.7.0",
|
|
@@ -114,5 +114,5 @@
|
|
|
114
114
|
"react-dom": ">= 16.8.6 < 17",
|
|
115
115
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
116
116
|
},
|
|
117
|
-
"gitHead": "
|
|
117
|
+
"gitHead": "ea33c4f9396f3843573b2508a93473bb3d98ee65"
|
|
118
118
|
}
|
package/src/api/queries.js
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { gql } from "@apollo/client";
|
|
2
2
|
import { TAG_DETAIL } from "./fragments";
|
|
3
3
|
|
|
4
|
+
export const LINK_LABELS_QUERY = gql`
|
|
5
|
+
query StructureToStructureLabels {
|
|
6
|
+
labels {
|
|
7
|
+
id
|
|
8
|
+
name
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
`;
|
|
12
|
+
|
|
4
13
|
export const TAG_QUERY = gql`
|
|
5
14
|
query Tag($id: ID!) {
|
|
6
15
|
tag(id: $id) {
|
|
@@ -166,6 +175,8 @@ export const STRUCTURE_STRUCTURE_LINKS_QUERY = gql`
|
|
|
166
175
|
dataStructure(id: $id) {
|
|
167
176
|
id
|
|
168
177
|
dataStructureLinks {
|
|
178
|
+
_actions
|
|
179
|
+
id
|
|
169
180
|
labels {
|
|
170
181
|
name
|
|
171
182
|
}
|
package/src/api.js
CHANGED
|
@@ -30,6 +30,9 @@ const API_PROFILE_GROUPS = "/api/profile_execution_groups";
|
|
|
30
30
|
const API_SYSTEM = "/api/systems/:id";
|
|
31
31
|
const API_SYSTEMS = "/api/systems";
|
|
32
32
|
const API_SYSTEM_STRUCTURES = "/api/systems/:id/structures";
|
|
33
|
+
const API_STRUCTURE_TO_STRUCTURE_LINKS = "/api/data_structure_links";
|
|
34
|
+
const API_STRUCTURE_TO_STRUCTURE_LINK =
|
|
35
|
+
"/api/data_structures/structure_links/source/:sourceStructureId/target/:targetStructureId";
|
|
33
36
|
const API_STRUCTURE_TYPE = "/api/data_structure_types/:id";
|
|
34
37
|
const API_STRUCTURE_TYPES = "/api/data_structure_types";
|
|
35
38
|
const API_STRUCTURES_DOMAINS_UPLOAD =
|
|
@@ -87,4 +90,6 @@ export {
|
|
|
87
90
|
API_STRUCTURES_UPLOAD_EVENTS,
|
|
88
91
|
API_STRUCTURE_NOTE,
|
|
89
92
|
API_STRUCTURE_NOTES,
|
|
93
|
+
API_STRUCTURE_TO_STRUCTURE_LINKS,
|
|
94
|
+
API_STRUCTURE_TO_STRUCTURE_LINK,
|
|
90
95
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { Form } from "semantic-ui-react";
|
|
5
|
+
import { useIntl, FormattedMessage } from "react-intl";
|
|
6
|
+
import { useQuery } from "@apollo/client";
|
|
7
|
+
import { LINK_LABELS_QUERY } from "../api/queries";
|
|
8
|
+
|
|
9
|
+
const makeOptions = (labels) =>
|
|
10
|
+
_.flow(
|
|
11
|
+
_.sortBy("name"),
|
|
12
|
+
_.map(({ id, name }) => ({
|
|
13
|
+
key: id,
|
|
14
|
+
value: id,
|
|
15
|
+
text: name,
|
|
16
|
+
}))
|
|
17
|
+
)(labels);
|
|
18
|
+
|
|
19
|
+
export const LinkLabelsSelector = ({
|
|
20
|
+
labels,
|
|
21
|
+
loading,
|
|
22
|
+
onChange,
|
|
23
|
+
selectedLabelIds,
|
|
24
|
+
}) => {
|
|
25
|
+
const { formatMessage } = useIntl();
|
|
26
|
+
const options = makeOptions(labels);
|
|
27
|
+
|
|
28
|
+
return _.isEmpty(labels) ? null : (
|
|
29
|
+
<Form.Field>
|
|
30
|
+
<label>
|
|
31
|
+
<FormattedMessage id="structureLink.selector.label" />
|
|
32
|
+
</label>
|
|
33
|
+
|
|
34
|
+
<Form.Dropdown
|
|
35
|
+
loading={loading}
|
|
36
|
+
multiple
|
|
37
|
+
name="link-label-selector"
|
|
38
|
+
onChange={(_e, { value: labels }) => {
|
|
39
|
+
onChange(labels);
|
|
40
|
+
}}
|
|
41
|
+
options={options}
|
|
42
|
+
placeholder={formatMessage({
|
|
43
|
+
id: loading ? "loading" : "structureLink.selector.placeholder",
|
|
44
|
+
})}
|
|
45
|
+
search
|
|
46
|
+
selection
|
|
47
|
+
value={selectedLabelIds}
|
|
48
|
+
/>
|
|
49
|
+
</Form.Field>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
LinkLabelsSelector.propTypes = {
|
|
54
|
+
labels: PropTypes.array,
|
|
55
|
+
loading: PropTypes.bool,
|
|
56
|
+
onChange: PropTypes.func,
|
|
57
|
+
selectedLabelIds: PropTypes.array,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const LinkLabelsSelectorLoader = (props) => {
|
|
61
|
+
const { error, data } = useQuery(LINK_LABELS_QUERY, {
|
|
62
|
+
fetchPolicy: "cache-and-network",
|
|
63
|
+
});
|
|
64
|
+
if (error) return null;
|
|
65
|
+
const labels = data?.labels || [];
|
|
66
|
+
return <LinkLabelsSelector {...props} labels={labels} />;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
LinkLabelsSelectorLoader.propTypes = {
|
|
70
|
+
onChange: PropTypes.func,
|
|
71
|
+
selectedLabelIds: PropTypes.array,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default LinkLabelsSelectorLoader;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { Suspense } from "react";
|
|
3
|
+
import { useForm, Controller } from "react-hook-form";
|
|
4
|
+
import { Button, Form } from "semantic-ui-react";
|
|
5
|
+
import { connect } from "react-redux";
|
|
6
|
+
import PropTypes from "prop-types";
|
|
7
|
+
import { useIntl } from "react-intl";
|
|
8
|
+
import { HistoryBackButton } from "@truedat/core/components";
|
|
9
|
+
import { linkStructureToStructure as linkStructureToStructureRoutine } from "../routines";
|
|
10
|
+
import LinkLabelsSelector from "./LinkLabelsSelector";
|
|
11
|
+
|
|
12
|
+
const StructureSelector = React.lazy(() =>
|
|
13
|
+
import("@truedat/dd/components/StructureSelector")
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const StructureStructureForm = ({
|
|
17
|
+
sourceStructure,
|
|
18
|
+
linkStructureToStructure,
|
|
19
|
+
}) => {
|
|
20
|
+
const { formatMessage } = useIntl();
|
|
21
|
+
|
|
22
|
+
const { handleSubmit, control, formState } = useForm({
|
|
23
|
+
mode: "all",
|
|
24
|
+
defaultValues: {
|
|
25
|
+
sourceStructureId: sourceStructure.id,
|
|
26
|
+
targetStructureId: null,
|
|
27
|
+
labelIds: [],
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
const { isDirty, isValid } = formState;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Suspense fallback={null}>
|
|
34
|
+
<Form
|
|
35
|
+
onSubmit={handleSubmit((data) => {
|
|
36
|
+
linkStructureToStructure(data);
|
|
37
|
+
})}
|
|
38
|
+
>
|
|
39
|
+
<Controller
|
|
40
|
+
control={control}
|
|
41
|
+
name="labelIds"
|
|
42
|
+
render={({ field: { onChange, value: selectedLabelIds } }) => (
|
|
43
|
+
<LinkLabelsSelector
|
|
44
|
+
onChange={onChange}
|
|
45
|
+
selectedLabelIds={selectedLabelIds}
|
|
46
|
+
/>
|
|
47
|
+
)}
|
|
48
|
+
/>
|
|
49
|
+
|
|
50
|
+
<Controller
|
|
51
|
+
control={control}
|
|
52
|
+
name="targetStructureId"
|
|
53
|
+
rules={{ required: true }}
|
|
54
|
+
render={({ field: { onChange } }) => (
|
|
55
|
+
<StructureSelector
|
|
56
|
+
onSelect={({ id }) => {
|
|
57
|
+
onChange(id);
|
|
58
|
+
}}
|
|
59
|
+
/>
|
|
60
|
+
)}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
<div className="actions">
|
|
64
|
+
<HistoryBackButton
|
|
65
|
+
content={formatMessage({ id: "actions.cancel" })}
|
|
66
|
+
/>
|
|
67
|
+
<Button
|
|
68
|
+
type="submit"
|
|
69
|
+
disabled={!isDirty || !isValid}
|
|
70
|
+
primary
|
|
71
|
+
content={formatMessage({ id: "actions.save" })}
|
|
72
|
+
/>
|
|
73
|
+
</div>
|
|
74
|
+
</Form>
|
|
75
|
+
</Suspense>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
StructureStructureForm.propTypes = {
|
|
80
|
+
sourceStructure: PropTypes.object,
|
|
81
|
+
linkStructureToStructure: PropTypes.func,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const mapDispatchToProps = {
|
|
85
|
+
linkStructureToStructure: linkStructureToStructureRoutine,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const mapStateToProps = ({ structure }) => ({
|
|
89
|
+
sourceStructure: structure,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export default connect(
|
|
93
|
+
mapStateToProps,
|
|
94
|
+
mapDispatchToProps
|
|
95
|
+
)(StructureStructureForm);
|
|
@@ -2,13 +2,18 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { FormattedMessage } from "react-intl";
|
|
5
|
-
import { Table, Grid } from "semantic-ui-react";
|
|
5
|
+
import { Button, Table, Grid } from "semantic-ui-react";
|
|
6
|
+
import { Link } from "react-router-dom";
|
|
7
|
+
import { linkTo } from "@truedat/core/routes";
|
|
6
8
|
import { columnDecorator } from "@truedat/core/services";
|
|
7
9
|
import { Loading } from "@truedat/core/components";
|
|
8
10
|
import { useQuery } from "@apollo/client";
|
|
9
11
|
import { useParams } from "react-router-dom";
|
|
12
|
+
import { connect } from "react-redux";
|
|
13
|
+
import { ConfirmModal } from "@truedat/core/components";
|
|
10
14
|
import { StructureCrumbs } from "../components/StructureCrumbs";
|
|
11
15
|
import { STRUCTURE_STRUCTURE_LINKS_QUERY } from "../api/queries";
|
|
16
|
+
import { deleteStructureStructureLink as deleteStructureStructureLinkRoutine } from "../routines";
|
|
12
17
|
|
|
13
18
|
// An arrow with labels
|
|
14
19
|
export const Edge = ({ structureId, structureLink }) => (
|
|
@@ -68,12 +73,51 @@ Edge.propTypes = {
|
|
|
68
73
|
structureLink: PropTypes.object,
|
|
69
74
|
};
|
|
70
75
|
|
|
76
|
+
export const DeleteLink = ({ deleteStructureStructureLink, structureLink }) => {
|
|
77
|
+
return !structureLink._actions.delete_struct_to_struct_link ? null : (
|
|
78
|
+
<ConfirmModal
|
|
79
|
+
icon="trash"
|
|
80
|
+
trigger={
|
|
81
|
+
<Button
|
|
82
|
+
id={`delete-link-${structureLink.source.id}-${structureLink.target.id}`}
|
|
83
|
+
icon="trash alternate"
|
|
84
|
+
color="red"
|
|
85
|
+
/>
|
|
86
|
+
}
|
|
87
|
+
header={
|
|
88
|
+
<FormattedMessage id="relations.actions.data_field.delete.confirmation.header" />
|
|
89
|
+
}
|
|
90
|
+
content={
|
|
91
|
+
<FormattedMessage id="relations.actions.data_field.delete.confirmation.content" />
|
|
92
|
+
}
|
|
93
|
+
onConfirm={() =>
|
|
94
|
+
_.flow(
|
|
95
|
+
({
|
|
96
|
+
source: { id: sourceStructureId },
|
|
97
|
+
target: { id: targetStructureId },
|
|
98
|
+
id: dataStructureLinkId,
|
|
99
|
+
}) => ({ dataStructureLinkId, sourceStructureId, targetStructureId }),
|
|
100
|
+
(destructuredLink) => deleteStructureStructureLink(destructuredLink)
|
|
101
|
+
)(structureLink)
|
|
102
|
+
}
|
|
103
|
+
onOpen={(e) => e.stopPropagation()}
|
|
104
|
+
onClose={(e) => e.stopPropagation()}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
DeleteLink.propTypes = {
|
|
110
|
+
structureLink: PropTypes.object,
|
|
111
|
+
deleteStructureStructureLink: PropTypes.func,
|
|
112
|
+
};
|
|
113
|
+
|
|
71
114
|
export const StructureStructureLinks = ({
|
|
72
115
|
structureId,
|
|
73
116
|
currentVersion,
|
|
74
117
|
structureLinks,
|
|
118
|
+
canLinkStructureToStructure,
|
|
75
119
|
}) => {
|
|
76
|
-
const
|
|
120
|
+
const fixedColumns = [
|
|
77
121
|
{
|
|
78
122
|
header: "structureLink.currentStructure",
|
|
79
123
|
fieldSelector: () => currentVersion.name,
|
|
@@ -107,6 +151,21 @@ export const StructureStructureLinks = ({
|
|
|
107
151
|
},
|
|
108
152
|
];
|
|
109
153
|
|
|
154
|
+
const DeleteLinkConnected = connect(null, {
|
|
155
|
+
deleteStructureStructureLink: deleteStructureStructureLinkRoutine,
|
|
156
|
+
})(DeleteLink);
|
|
157
|
+
|
|
158
|
+
const deleteButtonColumn = {
|
|
159
|
+
textAlign: "center",
|
|
160
|
+
name: "_actions",
|
|
161
|
+
fieldSelector: (structureLink) => structureLink,
|
|
162
|
+
fieldDecorator: (structureLink) =>
|
|
163
|
+
React.createElement(DeleteLinkConnected, { structureLink }),
|
|
164
|
+
};
|
|
165
|
+
const columns = canLinkStructureToStructure
|
|
166
|
+
? [...fixedColumns, deleteButtonColumn]
|
|
167
|
+
: fixedColumns;
|
|
168
|
+
|
|
110
169
|
const headerRow = columns.map(({ header }, key) => (
|
|
111
170
|
<Table.HeaderCell
|
|
112
171
|
key={key}
|
|
@@ -128,6 +187,17 @@ export const StructureStructureLinks = ({
|
|
|
128
187
|
|
|
129
188
|
return (
|
|
130
189
|
<Grid>
|
|
190
|
+
{structureId && canLinkStructureToStructure && (
|
|
191
|
+
<Grid.Column width={16}>
|
|
192
|
+
<Button
|
|
193
|
+
floated="right"
|
|
194
|
+
primary
|
|
195
|
+
as={Link}
|
|
196
|
+
to={linkTo.STRUCTURE_STRUCTURE_LINKS_NEW({ id: structureId })}
|
|
197
|
+
content={<FormattedMessage id="links.actions.create" />}
|
|
198
|
+
/>
|
|
199
|
+
</Grid.Column>
|
|
200
|
+
)}
|
|
131
201
|
{!_.isEmpty(structureLinks) && (
|
|
132
202
|
<Grid.Column width={16}>
|
|
133
203
|
<Table
|
|
@@ -145,8 +215,23 @@ StructureStructureLinks.propTypes = {
|
|
|
145
215
|
structureId: PropTypes.string,
|
|
146
216
|
currentVersion: PropTypes.object,
|
|
147
217
|
structureLinks: PropTypes.array,
|
|
218
|
+
canLinkStructureToStructure: PropTypes.bool,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const mapStateToProps = (state) => {
|
|
222
|
+
const { structureActions } = state;
|
|
223
|
+
return {
|
|
224
|
+
canLinkStructureToStructure: _.has(
|
|
225
|
+
"create_struct_to_struct_link",
|
|
226
|
+
structureActions
|
|
227
|
+
),
|
|
228
|
+
};
|
|
148
229
|
};
|
|
149
230
|
|
|
231
|
+
const StructureStructureLinksConnected = connect(mapStateToProps)(
|
|
232
|
+
StructureStructureLinks
|
|
233
|
+
);
|
|
234
|
+
|
|
150
235
|
export const StructuresStructureLinksLoader = () => {
|
|
151
236
|
const { id } = useParams();
|
|
152
237
|
const { loading, error, data } = useQuery(STRUCTURE_STRUCTURE_LINKS_QUERY, {
|
|
@@ -155,8 +240,9 @@ export const StructuresStructureLinksLoader = () => {
|
|
|
155
240
|
});
|
|
156
241
|
if (error) return null;
|
|
157
242
|
if (loading) return <Loading />;
|
|
243
|
+
|
|
158
244
|
return (
|
|
159
|
-
<
|
|
245
|
+
<StructureStructureLinksConnected
|
|
160
246
|
structureId={data?.dataStructure?.id}
|
|
161
247
|
structureLinks={data?.dataStructure?.dataStructureLinks}
|
|
162
248
|
currentVersion={data?.dataStructure?.currentVersion}
|
|
@@ -5,6 +5,7 @@ import { Route, Switch } from "react-router-dom";
|
|
|
5
5
|
import { ErrorBoundary } from "@truedat/core/components";
|
|
6
6
|
import {
|
|
7
7
|
STRUCTURE_LINKS_NEW,
|
|
8
|
+
STRUCTURE_STRUCTURE_LINKS_NEW,
|
|
8
9
|
STRUCTURE_NOTES_EDIT,
|
|
9
10
|
} from "@truedat/core/routes";
|
|
10
11
|
import { getActiveTab } from "../selectors/getActiveTab";
|
|
@@ -22,6 +23,7 @@ import StructureVersions from "./StructureVersions";
|
|
|
22
23
|
import StructureMetadata from "./StructureMetadata";
|
|
23
24
|
import StructureNotesLoader from "./StructureNotesLoader";
|
|
24
25
|
import StructureStructureLinks from "./StructureStructureLinks";
|
|
26
|
+
import StructureStructureForm from "./StructureStructureForm";
|
|
25
27
|
|
|
26
28
|
const StructureLinks = React.lazy(() =>
|
|
27
29
|
import("@truedat/lm/components/StructureLinks")
|
|
@@ -63,7 +65,15 @@ export const StructureTabPane = ({ activeTab, tabVisibility }) => {
|
|
|
63
65
|
<Route render={() => <StructureLinks />} />
|
|
64
66
|
</Switch>
|
|
65
67
|
)}
|
|
66
|
-
{activeTab === "structureLinks" &&
|
|
68
|
+
{activeTab === "structureLinks" && (
|
|
69
|
+
<Switch>
|
|
70
|
+
<Route
|
|
71
|
+
path={STRUCTURE_STRUCTURE_LINKS_NEW}
|
|
72
|
+
render={() => <StructureStructureForm />}
|
|
73
|
+
/>
|
|
74
|
+
<Route render={() => <StructureStructureLinks />} />
|
|
75
|
+
</Switch>
|
|
76
|
+
)}
|
|
67
77
|
{activeTab === "profile" && tabVisibility.profile && <StructureProfile />}
|
|
68
78
|
{activeTab === "parents" && <StructureParentRelations />}
|
|
69
79
|
{activeTab === "children" && <StructureChildrenRelations />}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
STRUCTURE_GRANTS,
|
|
8
8
|
STRUCTURE_IMPACT,
|
|
9
9
|
STRUCTURE_LINEAGE,
|
|
10
|
-
|
|
10
|
+
STRUCTURE_STRUCTURE_LINKS,
|
|
11
11
|
STRUCTURE_LINKS,
|
|
12
12
|
STRUCTURE_METADATA,
|
|
13
13
|
STRUCTURE_NOTES,
|
|
@@ -25,7 +25,7 @@ export const StructureTabPaneRoutes = ({}) => (
|
|
|
25
25
|
<Route path={STRUCTURE_VERSION_VERSIONS} component={StructureTabPane} />
|
|
26
26
|
<Route path={STRUCTURE_VERSION} component={StructureTabPane} />
|
|
27
27
|
<Route path={STRUCTURE_PROFILE} component={StructureTabPane} />
|
|
28
|
-
<Route path={
|
|
28
|
+
<Route path={STRUCTURE_STRUCTURE_LINKS} component={StructureTabPane} />
|
|
29
29
|
<Route path={STRUCTURE_LINKS} component={StructureTabPane} />
|
|
30
30
|
<Route path={STRUCTURE_VERSIONS} component={StructureTabPane} />
|
|
31
31
|
<Route path={STRUCTURE_EVENTS} component={StructureTabPane} />
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
STRUCTURE_IMPACT,
|
|
9
9
|
STRUCTURE_LINEAGE,
|
|
10
10
|
STRUCTURE_LINKS,
|
|
11
|
-
|
|
11
|
+
STRUCTURE_STRUCTURE_LINKS,
|
|
12
12
|
STRUCTURE_NOTES,
|
|
13
13
|
STRUCTURE_PARENTS,
|
|
14
14
|
STRUCTURE_PROFILE,
|
|
@@ -26,7 +26,7 @@ export const StructureTabRoutes = () => (
|
|
|
26
26
|
<Route path={STRUCTURE_VERSION} component={StructureTabs} />
|
|
27
27
|
<Route path={STRUCTURE_PROFILE} component={StructureTabs} />
|
|
28
28
|
<Route path={STRUCTURE_LINKS} component={StructureTabs} />
|
|
29
|
-
<Route path={
|
|
29
|
+
<Route path={STRUCTURE_STRUCTURE_LINKS} component={StructureTabs} />
|
|
30
30
|
<Route path={STRUCTURE_VERSIONS} component={StructureTabs} />
|
|
31
31
|
<Route path={STRUCTURE_EVENTS} component={StructureTabs} />
|
|
32
32
|
<Route path={STRUCTURE_METADATA} component={StructureTabs} />
|
|
@@ -109,7 +109,7 @@ export const StructureTabs = ({
|
|
|
109
109
|
{
|
|
110
110
|
active: activeTab === "structureLinks",
|
|
111
111
|
as: Link,
|
|
112
|
-
to: linkTo.
|
|
112
|
+
to: linkTo.STRUCTURE_STRUCTURE_LINKS(structure),
|
|
113
113
|
content: structureLinks
|
|
114
114
|
? formatMessage({ id: "tabs.dd.structureLinks" })
|
|
115
115
|
: false,
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor } from "@testing-library/react";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
5
|
+
import StructureStructureForm from "../StructureStructureForm";
|
|
6
|
+
import { linkStructureToStructure } from "../../routines";
|
|
7
|
+
import { LINK_LABELS_QUERY } from "../../api/queries";
|
|
8
|
+
|
|
9
|
+
const linkLabels = {
|
|
10
|
+
labels: [
|
|
11
|
+
{
|
|
12
|
+
id: "1",
|
|
13
|
+
name: "is_copy_from",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "2",
|
|
17
|
+
name: "reimu_ga_tsukutta",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "3",
|
|
21
|
+
name: "marisa_ga_tsukutta",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const linkLabelsMock = {
|
|
27
|
+
request: { query: LINK_LABELS_QUERY },
|
|
28
|
+
result: { data: linkLabels },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe("<StructureStructureForm />", () => {
|
|
32
|
+
const structure = {
|
|
33
|
+
alias: null,
|
|
34
|
+
confidential: false,
|
|
35
|
+
domain_ids: [1],
|
|
36
|
+
domains: [
|
|
37
|
+
{
|
|
38
|
+
external_id: "Acme",
|
|
39
|
+
id: 1,
|
|
40
|
+
name: "Acme",
|
|
41
|
+
parent_id: null,
|
|
42
|
+
parents: [],
|
|
43
|
+
updated_at: "2022-03-22 12:56:39.639127Z",
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
external_id: "external_id",
|
|
47
|
+
id: 67583,
|
|
48
|
+
inserted_at: "2019-11-27T08:35:44.000000Z",
|
|
49
|
+
updated_at: "2021-06-10T12:01:17.343065Z",
|
|
50
|
+
class: null,
|
|
51
|
+
classes: null,
|
|
52
|
+
data_structure_link_count: 0,
|
|
53
|
+
data_structure_type: {
|
|
54
|
+
metadata_views: [],
|
|
55
|
+
template_id: 9,
|
|
56
|
+
translation: null,
|
|
57
|
+
},
|
|
58
|
+
degree: null,
|
|
59
|
+
deleted_at: null,
|
|
60
|
+
description: null,
|
|
61
|
+
grant: null,
|
|
62
|
+
grants: [],
|
|
63
|
+
group: "group",
|
|
64
|
+
implementation_count: 0,
|
|
65
|
+
metadata: {
|
|
66
|
+
database: "database",
|
|
67
|
+
host: "host",
|
|
68
|
+
},
|
|
69
|
+
name: "structure",
|
|
70
|
+
note: {},
|
|
71
|
+
siblings: [],
|
|
72
|
+
source: {
|
|
73
|
+
external_id: "oracle_meta_ds2",
|
|
74
|
+
id: 19,
|
|
75
|
+
},
|
|
76
|
+
system: {
|
|
77
|
+
id: 2,
|
|
78
|
+
name: "SYSTEM_1",
|
|
79
|
+
},
|
|
80
|
+
type: "Schema",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
it("matches the latest snapshot", async () => {
|
|
84
|
+
const columns = ["name", "system", "group"].map((name) => ({ name }));
|
|
85
|
+
const structureActiveFilters = {};
|
|
86
|
+
const structuresRows = [{ id: 1 }, { id: 2 }];
|
|
87
|
+
const dispatch = jest.fn();
|
|
88
|
+
|
|
89
|
+
const props = {
|
|
90
|
+
linkStructureToStructure,
|
|
91
|
+
structuresRows,
|
|
92
|
+
columns,
|
|
93
|
+
onSelect: jest.fn(),
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const renderOpts = {
|
|
97
|
+
mocks: [linkLabelsMock],
|
|
98
|
+
state: {
|
|
99
|
+
structure,
|
|
100
|
+
structures: [structure],
|
|
101
|
+
structureActiveFilters,
|
|
102
|
+
},
|
|
103
|
+
dispatch,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const { queryByText, findByText, getByRole } = render(
|
|
107
|
+
<StructureStructureForm {...props} />,
|
|
108
|
+
renderOpts
|
|
109
|
+
);
|
|
110
|
+
await waitFor(() =>
|
|
111
|
+
expect(queryByText(/loading/i)).not.toBeInTheDocument()
|
|
112
|
+
);
|
|
113
|
+
userEvent.click(await findByText("structure"));
|
|
114
|
+
|
|
115
|
+
const saveButton = getByRole("button", { name: "Save" });
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(saveButton).toBeEnabled();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const label1 = getByRole("option", { name: "is_copy_from" });
|
|
121
|
+
const label2 = getByRole("option", { name: "reimu_ga_tsukutta" });
|
|
122
|
+
|
|
123
|
+
userEvent.click(label1);
|
|
124
|
+
userEvent.click(label2);
|
|
125
|
+
|
|
126
|
+
userEvent.click(saveButton);
|
|
127
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
128
|
+
|
|
129
|
+
expect(dispatch).toHaveBeenCalledWith({
|
|
130
|
+
...linkStructureToStructure(),
|
|
131
|
+
payload: {
|
|
132
|
+
sourceStructureId: 67583,
|
|
133
|
+
targetStructureId: 67583,
|
|
134
|
+
labelIds: ["1", "2"],
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|