@truedat/dd 4.54.9 → 4.54.11

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.54.11] 2022-11-14
4
+
5
+ ### Added
6
+
7
+ - [TD-5038] Links between structures tab
8
+
9
+ ## [4.54.10] 2022-11-10
10
+
11
+ ### Added
12
+
13
+ - [TD-5037] Highlights fields changes in structure notes
14
+
3
15
  ## [4.54.8] 2022-10-28
4
16
 
5
17
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "4.54.9",
3
+ "version": "4.54.11",
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.4",
35
35
  "@testing-library/react": "^12.0.0",
36
36
  "@testing-library/user-event": "^13.2.1",
37
- "@truedat/test": "4.54.9",
37
+ "@truedat/test": "4.54.11",
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",
@@ -88,9 +88,9 @@
88
88
  },
89
89
  "dependencies": {
90
90
  "@apollo/client": "^3.7.0",
91
- "@truedat/auth": "4.54.9",
92
- "@truedat/core": "4.54.9",
93
- "@truedat/df": "4.54.9",
91
+ "@truedat/auth": "4.54.11",
92
+ "@truedat/core": "4.54.11",
93
+ "@truedat/df": "4.54.11",
94
94
  "lodash": "^4.17.21",
95
95
  "path-to-regexp": "^1.7.0",
96
96
  "prop-types": "^15.8.1",
@@ -112,5 +112,5 @@
112
112
  "react-dom": ">= 16.8.6 < 17",
113
113
  "semantic-ui-react": ">= 0.88.2 < 2.1"
114
114
  },
115
- "gitHead": "27f711249723050708a6f6f4de3ab5029069c4d7"
115
+ "gitHead": "72603ee10a5ad665f0c742e33a6014de94c4ba53"
116
116
  }
@@ -119,3 +119,48 @@ export const STRUCTURE_NOTES_QUERY = gql`
119
119
  }
120
120
  }
121
121
  `;
122
+
123
+ export const STRUCTURE_STRUCTURE_LINKS_QUERY = gql`
124
+ query DataStructureLinks($id: ID!) {
125
+ dataStructure(id: $id) {
126
+ id
127
+ dataStructureLinks {
128
+ labels {
129
+ name
130
+ }
131
+ source {
132
+ id
133
+ currentVersion {
134
+ name
135
+ pathWithIds {
136
+ data_structure_id
137
+ name
138
+ }
139
+ }
140
+ system {
141
+ id
142
+ name
143
+ }
144
+ }
145
+ target {
146
+ id
147
+ currentVersion {
148
+ name
149
+ pathWithIds {
150
+ data_structure_id
151
+ name
152
+ }
153
+ }
154
+ system {
155
+ id
156
+ name
157
+ }
158
+ }
159
+ }
160
+ currentVersion {
161
+ id
162
+ name
163
+ }
164
+ }
165
+ }
166
+ `;
@@ -30,7 +30,7 @@ export const SystemCrumb = ({ id, name }) => (
30
30
  );
31
31
 
32
32
  SystemCrumb.propTypes = {
33
- id: PropTypes.number,
33
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
34
34
  name: PropTypes.string,
35
35
  };
36
36
 
@@ -44,31 +44,24 @@ export const AncestorCrumb = ({ name, id }) => (
44
44
  );
45
45
 
46
46
  AncestorCrumb.propTypes = {
47
- id: PropTypes.number,
47
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
48
48
  name: PropTypes.string,
49
49
  };
50
50
 
51
- export const StructureCrumb = ({ name, id, version }) =>
52
- _.isNil(version) ? (
53
- <Breadcrumb.Section active content={name} />
51
+ export const StructureCrumb = ({ name, id, isStructureCrumbActive }) => {
52
+ return isStructureCrumbActive ? (
53
+ <Breadcrumb.Section as={Link} to={linkTo.STRUCTURE({ id })} active>
54
+ {name}
55
+ </Breadcrumb.Section>
54
56
  ) : (
55
- <>
56
- <Breadcrumb.Section
57
- as={Link}
58
- to={linkTo.STRUCTURE({ id })}
59
- active={false}
60
- >
61
- {name}
62
- </Breadcrumb.Section>
63
- <Breadcrumb.Divider icon="right angle" />
64
- </>
57
+ <Breadcrumb.Section active content={name} />
65
58
  );
59
+ };
66
60
 
67
61
  StructureCrumb.propTypes = {
68
- id: PropTypes.number,
69
- alias: PropTypes.string,
62
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
70
63
  name: PropTypes.string,
71
- version: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
64
+ isStructureCrumbActive: PropTypes.bool,
72
65
  };
73
66
 
74
67
  export const VersionCrumb = ({ version }) => (
@@ -88,6 +81,7 @@ export const StructureCrumbs = ({
88
81
  alias,
89
82
  name,
90
83
  version,
84
+ isStructureCrumbActive,
91
85
  }) =>
92
86
  name ? (
93
87
  <Breadcrumb>
@@ -106,6 +100,7 @@ export const StructureCrumbs = ({
106
100
  name={_.defaultTo(name)(alias)}
107
101
  id={id}
108
102
  version={version}
103
+ isStructureCrumbActive={isStructureCrumbActive}
109
104
  />
110
105
  {_.isNil(version) ? null : <VersionCrumb id={id} version={version} />}
111
106
  </Breadcrumb>
@@ -113,12 +108,13 @@ export const StructureCrumbs = ({
113
108
 
114
109
  StructureCrumbs.propTypes = {
115
110
  ancestry: PropTypes.array,
116
- id: PropTypes.number,
111
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
117
112
  name: PropTypes.string,
118
113
  alias: PropTypes.string,
119
114
  field: PropTypes.object,
120
115
  system: PropTypes.object,
121
116
  version: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
117
+ isStructureCrumbActive: PropTypes.bool,
122
118
  };
123
119
 
124
120
  const mapStateToProps = ({
@@ -77,6 +77,7 @@ const StructureNote = ({ structureNote, template, structure, notesSize }) => {
77
77
  <DynamicFormViewer
78
78
  template={template}
79
79
  content={structureNote.df_content}
80
+ diff={structureNote._diff}
80
81
  />
81
82
  )}
82
83
  </Segment>
@@ -0,0 +1,167 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { FormattedMessage } from "react-intl";
5
+ import { Table, Grid } from "semantic-ui-react";
6
+ import { columnDecorator } from "@truedat/core/services";
7
+ import { Loading } from "@truedat/core/components";
8
+ import { useQuery } from "@apollo/client";
9
+ import { useParams } from "react-router-dom";
10
+ import { StructureCrumbs } from "../components/StructureCrumbs";
11
+ import { STRUCTURE_STRUCTURE_LINKS_QUERY } from "../api/queries";
12
+
13
+ // An arrow with labels
14
+ export const Edge = ({ structureId, structureLink }) => (
15
+ <svg
16
+ viewBox="0 0 100 10"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="200"
19
+ height="40"
20
+ >
21
+ <defs>
22
+ <marker
23
+ id="arrow"
24
+ markerWidth="6"
25
+ markerHeight="5"
26
+ refX="0"
27
+ refY="2"
28
+ orient="auto"
29
+ markerUnits="strokeWidth"
30
+ >
31
+ <path d="M0,0 L0,4 L5,2 z" fill="#7d6060" />
32
+ </marker>
33
+ </defs>
34
+ <path
35
+ id="p0"
36
+ markerEnd="url(#arrow)"
37
+ fill="none"
38
+ d={
39
+ structureId === structureLink?.source.id
40
+ ? "M 0,10 h 90"
41
+ : structureId === structureLink?.target.id
42
+ ? "M 95,10 h -90"
43
+ : ""
44
+ }
45
+ strokeWidth="1"
46
+ stroke="#871f1f"
47
+ />
48
+ <path id="path-for-text" fill="none" d="M 0,4 h 90" />
49
+ <text fontSize="8">
50
+ <textPath
51
+ startOffset="50%"
52
+ y="10%"
53
+ textAnchor="middle"
54
+ dominantBaseline="middle"
55
+ href="#path-for-text"
56
+ >
57
+ {_.flow(
58
+ _.map((label) => label?.name),
59
+ _.join(", ")
60
+ )(structureLink?.labels)}
61
+ </textPath>
62
+ </text>
63
+ </svg>
64
+ );
65
+
66
+ Edge.propTypes = {
67
+ structureId: PropTypes.string,
68
+ structureLink: PropTypes.object,
69
+ };
70
+
71
+ export const StructureStructureLinks = ({
72
+ structureId,
73
+ currentVersion,
74
+ structureLinks,
75
+ }) => {
76
+ const columns = [
77
+ {
78
+ header: "structureLink.currentStructure",
79
+ fieldSelector: () => currentVersion.name,
80
+ },
81
+ {
82
+ header: "structureLink.label",
83
+ fieldSelector: (structureLink) => Edge({ structureId, structureLink }),
84
+ },
85
+ {
86
+ header: "structureLink.relatedStructure",
87
+ fieldSelector: (structureLink) =>
88
+ _.flow(
89
+ () =>
90
+ structureId !== structureLink?.source.id
91
+ ? structureLink.source
92
+ : structureId !== structureLink?.target.id
93
+ ? structureLink.target
94
+ : null,
95
+ (relatedStructure) => (
96
+ <StructureCrumbs
97
+ {...{
98
+ id: relatedStructure?.id,
99
+ name: relatedStructure?.currentVersion.name,
100
+ system: relatedStructure?.system,
101
+ ancestry: relatedStructure?.currentVersion.pathWithIds,
102
+ isStructureCrumbActive: true,
103
+ }}
104
+ />
105
+ )
106
+ )(structureId, structureLink),
107
+ },
108
+ ];
109
+
110
+ const headerRow = columns.map(({ header }, key) => (
111
+ <Table.HeaderCell
112
+ key={key}
113
+ content={header ? <FormattedMessage id={header} /> : null}
114
+ />
115
+ ));
116
+
117
+ const renderBodyRow = (structureLink) => (
118
+ <Table.Row key={`${structureLink.source.id}_${structureLink.target.id}`}>
119
+ {columns.map((column, i) => (
120
+ <Table.Cell
121
+ key={i}
122
+ textAlign={_.prop("textAlign")(column)}
123
+ content={columnDecorator(column)(structureLink)}
124
+ />
125
+ ))}
126
+ </Table.Row>
127
+ );
128
+
129
+ return (
130
+ <Grid>
131
+ {!_.isEmpty(structureLinks) && (
132
+ <Grid.Column width={16}>
133
+ <Table
134
+ headerRow={headerRow}
135
+ renderBodyRow={renderBodyRow}
136
+ tableData={structureLinks}
137
+ />
138
+ </Grid.Column>
139
+ )}
140
+ </Grid>
141
+ );
142
+ };
143
+
144
+ StructureStructureLinks.propTypes = {
145
+ structureId: PropTypes.string,
146
+ currentVersion: PropTypes.object,
147
+ structureLinks: PropTypes.array,
148
+ };
149
+
150
+ export const StructuresStructureLinksLoader = () => {
151
+ const { id } = useParams();
152
+ const { loading, error, data } = useQuery(STRUCTURE_STRUCTURE_LINKS_QUERY, {
153
+ fetchPolicy: "cache-and-network",
154
+ variables: { id },
155
+ });
156
+ if (error) return null;
157
+ if (loading) return <Loading />;
158
+ return (
159
+ <StructureStructureLinks
160
+ structureId={data?.dataStructure?.id}
161
+ structureLinks={data?.dataStructure?.dataStructureLinks}
162
+ currentVersion={data?.dataStructure?.currentVersion}
163
+ />
164
+ );
165
+ };
166
+
167
+ export default StructuresStructureLinksLoader;
@@ -21,6 +21,7 @@ import StructureParentRelations from "./StructureParentRelations";
21
21
  import StructureVersions from "./StructureVersions";
22
22
  import StructureMetadata from "./StructureMetadata";
23
23
  import StructureNotesLoader from "./StructureNotesLoader";
24
+ import StructureStructureLinks from "./StructureStructureLinks";
24
25
 
25
26
  const StructureLinks = React.lazy(() =>
26
27
  import("@truedat/lm/components/StructureLinks")
@@ -36,46 +37,49 @@ const RuleImplementationsTable = React.lazy(() =>
36
37
  import("@truedat/dq/components/RuleImplementationsTable")
37
38
  );
38
39
 
39
- export const StructureTabPane = ({ activeTab, tabVisibility }) => (
40
- <ErrorBoundary>
41
- {activeTab === "fields" && <StructureFields />}
42
- {activeTab === "notes" && (
43
- <>
44
- <StructureNotesLoader scope="dd" />
40
+ export const StructureTabPane = ({ activeTab, tabVisibility }) => {
41
+ return (
42
+ <ErrorBoundary>
43
+ {activeTab === "fields" && <StructureFields />}
44
+ {activeTab === "notes" && (
45
+ <>
46
+ <StructureNotesLoader scope="dd" />
47
+ <Switch>
48
+ <Route
49
+ path={STRUCTURE_NOTES_EDIT}
50
+ render={() => <StructureNotesEdit />}
51
+ />
52
+ <Route render={() => <StructureNotes />} />
53
+ </Switch>
54
+ </>
55
+ )}
56
+ {activeTab === "grants" && tabVisibility.grants && <StructureGrants />}
57
+ {activeTab === "links" && (
45
58
  <Switch>
46
59
  <Route
47
- path={STRUCTURE_NOTES_EDIT}
48
- render={() => <StructureNotesEdit />}
60
+ path={STRUCTURE_LINKS_NEW}
61
+ render={() => <StructureRelationForm />}
49
62
  />
50
- <Route render={() => <StructureNotes />} />
63
+ <Route render={() => <StructureLinks />} />
51
64
  </Switch>
52
- </>
53
- )}
54
- {activeTab === "grants" && tabVisibility.grants && <StructureGrants />}
55
- {activeTab === "links" && (
56
- <Switch>
57
- <Route
58
- path={STRUCTURE_LINKS_NEW}
59
- render={() => <StructureRelationForm />}
60
- />
61
- <Route render={() => <StructureLinks />} />
62
- </Switch>
63
- )}
64
- {activeTab === "profile" && tabVisibility.profile && <StructureProfile />}
65
- {activeTab === "parents" && <StructureParentRelations />}
66
- {activeTab === "children" && <StructureChildrenRelations />}
67
- {activeTab === "versions" && <StructureVersions />}
68
- {activeTab === "events" && <StructureEvents />}
69
- {activeTab === "rules" && tabVisibility.rules && (
70
- <RuleImplementationsTable withoutColumns={["status"]} />
71
- )}
72
- {activeTab === "metadata" && tabVisibility.metadata && (
73
- <StructureMetadata />
74
- )}
75
- {activeTab === "lineage" && <StructureLineage />}
76
- {activeTab === "impact" && <StructureImpact />}
77
- </ErrorBoundary>
78
- );
65
+ )}
66
+ {activeTab === "structureLinks" && <StructureStructureLinks />}
67
+ {activeTab === "profile" && tabVisibility.profile && <StructureProfile />}
68
+ {activeTab === "parents" && <StructureParentRelations />}
69
+ {activeTab === "children" && <StructureChildrenRelations />}
70
+ {activeTab === "versions" && <StructureVersions />}
71
+ {activeTab === "events" && <StructureEvents />}
72
+ {activeTab === "rules" && tabVisibility.rules && (
73
+ <RuleImplementationsTable withoutColumns={["status"]} />
74
+ )}
75
+ {activeTab === "metadata" && tabVisibility.metadata && (
76
+ <StructureMetadata />
77
+ )}
78
+ {activeTab === "lineage" && <StructureLineage />}
79
+ {activeTab === "impact" && <StructureImpact />}
80
+ </ErrorBoundary>
81
+ );
82
+ };
79
83
 
80
84
  StructureTabPane.propTypes = {
81
85
  activeTab: PropTypes.string,
@@ -7,6 +7,7 @@ import {
7
7
  STRUCTURE_GRANTS,
8
8
  STRUCTURE_IMPACT,
9
9
  STRUCTURE_LINEAGE,
10
+ STRUCTURE_LINKED_STRUCTURES,
10
11
  STRUCTURE_LINKS,
11
12
  STRUCTURE_METADATA,
12
13
  STRUCTURE_NOTES,
@@ -24,6 +25,7 @@ export const StructureTabPaneRoutes = ({}) => (
24
25
  <Route path={STRUCTURE_VERSION_VERSIONS} component={StructureTabPane} />
25
26
  <Route path={STRUCTURE_VERSION} component={StructureTabPane} />
26
27
  <Route path={STRUCTURE_PROFILE} component={StructureTabPane} />
28
+ <Route path={STRUCTURE_LINKED_STRUCTURES} component={StructureTabPane} />
27
29
  <Route path={STRUCTURE_LINKS} component={StructureTabPane} />
28
30
  <Route path={STRUCTURE_VERSIONS} component={StructureTabPane} />
29
31
  <Route path={STRUCTURE_EVENTS} component={StructureTabPane} />
@@ -8,6 +8,7 @@ import {
8
8
  STRUCTURE_IMPACT,
9
9
  STRUCTURE_LINEAGE,
10
10
  STRUCTURE_LINKS,
11
+ STRUCTURE_LINKED_STRUCTURES,
11
12
  STRUCTURE_NOTES,
12
13
  STRUCTURE_PARENTS,
13
14
  STRUCTURE_PROFILE,
@@ -25,6 +26,7 @@ export const StructureTabRoutes = () => (
25
26
  <Route path={STRUCTURE_VERSION} component={StructureTabs} />
26
27
  <Route path={STRUCTURE_PROFILE} component={StructureTabs} />
27
28
  <Route path={STRUCTURE_LINKS} component={StructureTabs} />
29
+ <Route path={STRUCTURE_LINKED_STRUCTURES} component={StructureTabs} />
28
30
  <Route path={STRUCTURE_VERSIONS} component={StructureTabs} />
29
31
  <Route path={STRUCTURE_EVENTS} component={StructureTabs} />
30
32
  <Route path={STRUCTURE_METADATA} component={StructureTabs} />
@@ -30,6 +30,7 @@ export const StructureTabs = ({
30
30
  impact,
31
31
  lineage,
32
32
  links,
33
+ structureLinks,
33
34
  metadata,
34
35
  notes,
35
36
  parents,
@@ -105,6 +106,15 @@ export const StructureTabs = ({
105
106
  content: links ? formatMessage({ id: "tabs.dd.links" }) : false,
106
107
  key: "links",
107
108
  },
109
+ {
110
+ active: activeTab === "structureLinks",
111
+ as: Link,
112
+ to: linkTo.STRUCTURE_LINKED_STRUCTURES(structure),
113
+ content: structureLinks
114
+ ? formatMessage({ id: "tabs.dd.structureLinks" })
115
+ : false,
116
+ key: "structureLinks",
117
+ },
108
118
  {
109
119
  active: activeTab === "lineage",
110
120
  as: Link,
@@ -47,4 +47,63 @@ describe("<StructureNotes />", () => {
47
47
 
48
48
  expect(container).toMatchSnapshot();
49
49
  });
50
+
51
+ it("matches the latest snapshot with draft structure note", async () => {
52
+ const props = {
53
+ structure: { id: 1 },
54
+ structureNotes: { _actions: { foo: "bar" }, loading: false },
55
+ activeStructureNotes: [
56
+ {
57
+ id: 3,
58
+ status: "draft",
59
+ df_content: { foo: "xyz" },
60
+ version: "3",
61
+ _diff: ["foo"],
62
+ },
63
+ {
64
+ id: 2,
65
+ status: "published",
66
+ df_content: { foo: "bar" },
67
+ version: "2",
68
+ },
69
+ ],
70
+ versionedStructureNotes: [
71
+ {
72
+ id: 1,
73
+ status: "versioned",
74
+ df_content: { foo: "bar1" },
75
+ version: "1",
76
+ },
77
+ ],
78
+ template: {
79
+ id: 2,
80
+ content: [
81
+ {
82
+ fields: [
83
+ {
84
+ cardinality: "?",
85
+ default: "",
86
+ label: "foo",
87
+ name: "foo",
88
+ type: "string",
89
+ values: null,
90
+ widget: "text",
91
+ value: "foo value",
92
+ required: false,
93
+ },
94
+ ],
95
+ name: "foo group",
96
+ },
97
+ ],
98
+ scope: "dd",
99
+ },
100
+ doStructureNoteAction: () => {},
101
+ };
102
+ const { findByText, container } = render(<StructureNotes {...props} />);
103
+ expect(container).toMatchSnapshot();
104
+
105
+ userEvent.click(await findByText("Show content"));
106
+
107
+ expect(container).toMatchSnapshot();
108
+ });
50
109
  });
@@ -0,0 +1,161 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { waitFor } from "@testing-library/react";
4
+ import { useParams } from "react-router-dom";
5
+ import { STRUCTURE_STRUCTURE_LINKS_QUERY } from "../../api/queries";
6
+ import StructureStructureLinks from "../StructureStructureLinks";
7
+
8
+ jest.mock("react-router-dom", () => ({
9
+ ...jest.requireActual("react-router-dom"),
10
+ useParams: jest.fn(),
11
+ }));
12
+
13
+ const dataStructureWithLinks = {
14
+ currentVersion: {
15
+ id: "212416",
16
+ name: "CMESANO",
17
+ },
18
+ dataStructureLinks: [
19
+ {
20
+ labels: [
21
+ {
22
+ name: "copy",
23
+ },
24
+ ],
25
+ source: {
26
+ currentVersion: {
27
+ name: "CMESANO",
28
+ pathWithIds: [
29
+ {
30
+ data_structure_id: "114207",
31
+ name: "SRV_DS2P.PRO.SANTALUCIA.ES",
32
+ },
33
+ {
34
+ data_structure_id: "58050",
35
+ name: "SIISDS2",
36
+ },
37
+ {
38
+ data_structure_id: "58045",
39
+ name: "INPAHEM_SINIEPAGO",
40
+ },
41
+ ],
42
+ },
43
+ id: "60217",
44
+ system: {
45
+ id: "2",
46
+ name: "Oracle producción solvencia",
47
+ },
48
+ },
49
+ target: {
50
+ currentVersion: {
51
+ name: "NIDSINIE",
52
+ pathWithIds: [
53
+ {
54
+ data_structure_id: "114207",
55
+ name: "SRV_DS2P.PRO.SANTALUCIA.ES",
56
+ },
57
+ {
58
+ data_structure_id: "58050",
59
+ name: "SIISDS2",
60
+ },
61
+ {
62
+ data_structure_id: "58045",
63
+ name: "INPAHEM_SINIEPAGO",
64
+ },
65
+ ],
66
+ },
67
+ id: "60218",
68
+ system: {
69
+ id: "2",
70
+ name: "Oracle producción solvencia",
71
+ },
72
+ },
73
+ },
74
+ {
75
+ labels: [
76
+ {
77
+ name: "label_1_1",
78
+ },
79
+ {
80
+ name: "label_1_2",
81
+ },
82
+ ],
83
+ source: {
84
+ currentVersion: {
85
+ name: "ivalindm",
86
+ pathWithIds: [
87
+ {
88
+ data_structure_id: "147587",
89
+ name: "bdrs",
90
+ },
91
+ {
92
+ data_structure_id: "151648",
93
+ name: "inpahem_siniepago",
94
+ },
95
+ ],
96
+ },
97
+ id: "151674",
98
+ system: {
99
+ id: "11",
100
+ name: "Azure Databricks",
101
+ },
102
+ },
103
+ target: {
104
+ currentVersion: {
105
+ name: "CMESANO",
106
+ pathWithIds: [
107
+ {
108
+ data_structure_id: "114207",
109
+ name: "SRV_DS2P.PRO.SANTALUCIA.ES",
110
+ },
111
+ {
112
+ data_structure_id: "58050",
113
+ name: "SIISDS2",
114
+ },
115
+ {
116
+ data_structure_id: "58045",
117
+ name: "INPAHEM_SINIEPAGO",
118
+ },
119
+ ],
120
+ },
121
+ id: "60217",
122
+ system: {
123
+ id: "2",
124
+ name: "Oracle producción solvencia",
125
+ },
126
+ },
127
+ },
128
+ ],
129
+ id: "60217",
130
+ };
131
+
132
+ const linksMock = {
133
+ request: {
134
+ query: STRUCTURE_STRUCTURE_LINKS_QUERY,
135
+ variables: { id: 60217 },
136
+ },
137
+ result: { data: { dataStructure: dataStructureWithLinks } },
138
+ };
139
+
140
+ describe("<StructureStructureLinks />", () => {
141
+ const props = { dataStructureWithLinks };
142
+
143
+ it("matches the latest snapshot", async () => {
144
+ const renderOpts = {
145
+ routes: ["/structures/60217/structureLinks"],
146
+ mocks: [linksMock],
147
+ };
148
+ useParams.mockImplementation(() => ({
149
+ id: 60217,
150
+ }));
151
+ const { container, findByText, getByRole, queryByText, debug } = render(
152
+ <StructureStructureLinks {...props} />,
153
+ renderOpts
154
+ );
155
+ await waitFor(() => {
156
+ expect(getByRole("link", { name: /NIDSINIE/i })).toBeInTheDocument();
157
+ expect(getByRole("link", { name: /ivalindm/i })).toBeInTheDocument();
158
+ });
159
+ expect(container).toMatchSnapshot();
160
+ });
161
+ });
@@ -143,3 +143,140 @@ exports[`<StructureNotes /> matches the latest snapshot 3`] = `
143
143
  </div>
144
144
  </div>
145
145
  `;
146
+
147
+ exports[`<StructureNotes /> matches the latest snapshot with draft structure note 1`] = `
148
+ <div>
149
+ <div
150
+ class="ui bottom attached segment"
151
+ >
152
+ <div
153
+ class="ui segment"
154
+ >
155
+ <div
156
+ style="display: flex; justify-content: space-between; align-items: center;"
157
+ >
158
+ <div>
159
+ <div
160
+ class="ui label basic"
161
+ >
162
+ Draft
163
+ </div>
164
+ <div
165
+ class="ui label basic"
166
+ >
167
+ Version: 3
168
+ </div>
169
+ </div>
170
+ <div>
171
+ <button
172
+ class="ui button"
173
+ >
174
+ Show content
175
+ </button>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ <div
180
+ class="ui segment"
181
+ >
182
+ <div
183
+ style="display: flex; justify-content: space-between; align-items: center;"
184
+ >
185
+ <div>
186
+ <div
187
+ class="ui label basic"
188
+ >
189
+ Published
190
+ </div>
191
+ <div
192
+ class="ui label basic"
193
+ >
194
+ Version: 2
195
+ </div>
196
+ </div>
197
+ <div />
198
+ </div>
199
+ <div>
200
+ DynamicFormViewer
201
+ </div>
202
+ </div>
203
+ <div>
204
+ <button
205
+ class="ui button"
206
+ >
207
+ Show previous versions
208
+ </button>
209
+ </div>
210
+ </div>
211
+ </div>
212
+ `;
213
+
214
+ exports[`<StructureNotes /> matches the latest snapshot with draft structure note 2`] = `
215
+ <div>
216
+ <div
217
+ class="ui bottom attached segment"
218
+ >
219
+ <div
220
+ class="ui segment"
221
+ >
222
+ <div
223
+ style="display: flex; justify-content: space-between; align-items: center;"
224
+ >
225
+ <div>
226
+ <div
227
+ class="ui label basic"
228
+ >
229
+ Draft
230
+ </div>
231
+ <div
232
+ class="ui label basic"
233
+ >
234
+ Version: 3
235
+ </div>
236
+ </div>
237
+ <div>
238
+ <button
239
+ class="ui button"
240
+ >
241
+ Hide content
242
+ </button>
243
+ </div>
244
+ </div>
245
+ <div>
246
+ DynamicFormViewer
247
+ </div>
248
+ </div>
249
+ <div
250
+ class="ui segment"
251
+ >
252
+ <div
253
+ style="display: flex; justify-content: space-between; align-items: center;"
254
+ >
255
+ <div>
256
+ <div
257
+ class="ui label basic"
258
+ >
259
+ Published
260
+ </div>
261
+ <div
262
+ class="ui label basic"
263
+ >
264
+ Version: 2
265
+ </div>
266
+ </div>
267
+ <div />
268
+ </div>
269
+ <div>
270
+ DynamicFormViewer
271
+ </div>
272
+ </div>
273
+ <div>
274
+ <button
275
+ class="ui button"
276
+ >
277
+ Show previous versions
278
+ </button>
279
+ </div>
280
+ </div>
281
+ </div>
282
+ `;
@@ -0,0 +1,287 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<StructureStructureLinks /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui grid"
7
+ >
8
+ <div
9
+ class="sixteen wide column"
10
+ >
11
+ <table
12
+ class="ui table"
13
+ >
14
+ <thead
15
+ class=""
16
+ >
17
+ <tr
18
+ class=""
19
+ >
20
+ <th
21
+ class=""
22
+ >
23
+ Current structure
24
+ </th>
25
+ <th
26
+ class=""
27
+ >
28
+ Relation label
29
+ </th>
30
+ <th
31
+ class=""
32
+ >
33
+ Related structure
34
+ </th>
35
+ </tr>
36
+ </thead>
37
+ <tbody
38
+ class=""
39
+ >
40
+ <tr
41
+ class=""
42
+ >
43
+ <td
44
+ class=""
45
+ >
46
+ CMESANO
47
+ </td>
48
+ <td
49
+ class=""
50
+ >
51
+ <svg
52
+ height="40"
53
+ viewBox="0 0 100 10"
54
+ width="200"
55
+ xmlns="http://www.w3.org/2000/svg"
56
+ >
57
+ <defs>
58
+ <marker
59
+ id="arrow"
60
+ markerHeight="5"
61
+ markerUnits="strokeWidth"
62
+ markerWidth="6"
63
+ orient="auto"
64
+ refX="0"
65
+ refY="2"
66
+ >
67
+ <path
68
+ d="M0,0 L0,4 L5,2 z"
69
+ fill="#7d6060"
70
+ />
71
+ </marker>
72
+ </defs>
73
+ <path
74
+ d="M 0,10 h 90"
75
+ fill="none"
76
+ id="p0"
77
+ marker-end="url(#arrow)"
78
+ stroke="#871f1f"
79
+ stroke-width="1"
80
+ />
81
+ <path
82
+ d="M 0,4 h 90"
83
+ fill="none"
84
+ id="path-for-text"
85
+ />
86
+ <text
87
+ font-size="8"
88
+ >
89
+ <textpath
90
+ dominant-baseline="middle"
91
+ href="#path-for-text"
92
+ startOffset="50%"
93
+ text-anchor="middle"
94
+ y="10%"
95
+ >
96
+ copy
97
+ </textpath>
98
+ </text>
99
+ </svg>
100
+ </td>
101
+ <td
102
+ class=""
103
+ >
104
+ <div
105
+ class="ui breadcrumb"
106
+ >
107
+ <a
108
+ class="section"
109
+ href="/structures"
110
+ >
111
+ Structures
112
+ </a>
113
+ <i
114
+ aria-hidden="true"
115
+ class="right angle icon divider"
116
+ />
117
+ <a
118
+ class="section"
119
+ href="/systems/2/structures"
120
+ >
121
+ Oracle producción solvencia
122
+ </a>
123
+ <i
124
+ aria-hidden="true"
125
+ class="right angle icon divider"
126
+ />
127
+ <a
128
+ class="section"
129
+ href="/structures/114207"
130
+ >
131
+ SRV_DS2P.PRO.SANTALUCIA.ES
132
+ </a>
133
+ <i
134
+ aria-hidden="true"
135
+ class="right angle icon divider"
136
+ />
137
+ <a
138
+ class="section"
139
+ href="/structures/58050"
140
+ >
141
+ SIISDS2
142
+ </a>
143
+ <i
144
+ aria-hidden="true"
145
+ class="right angle icon divider"
146
+ />
147
+ <a
148
+ class="section"
149
+ href="/structures/58045"
150
+ >
151
+ INPAHEM_SINIEPAGO
152
+ </a>
153
+ <i
154
+ aria-hidden="true"
155
+ class="right angle icon divider"
156
+ />
157
+ <a
158
+ class="active section"
159
+ href="/structures/60218"
160
+ >
161
+ NIDSINIE
162
+ </a>
163
+ </div>
164
+ </td>
165
+ </tr>
166
+ <tr
167
+ class=""
168
+ >
169
+ <td
170
+ class=""
171
+ >
172
+ CMESANO
173
+ </td>
174
+ <td
175
+ class=""
176
+ >
177
+ <svg
178
+ height="40"
179
+ viewBox="0 0 100 10"
180
+ width="200"
181
+ xmlns="http://www.w3.org/2000/svg"
182
+ >
183
+ <defs>
184
+ <marker
185
+ id="arrow"
186
+ markerHeight="5"
187
+ markerUnits="strokeWidth"
188
+ markerWidth="6"
189
+ orient="auto"
190
+ refX="0"
191
+ refY="2"
192
+ >
193
+ <path
194
+ d="M0,0 L0,4 L5,2 z"
195
+ fill="#7d6060"
196
+ />
197
+ </marker>
198
+ </defs>
199
+ <path
200
+ d="M 95,10 h -90"
201
+ fill="none"
202
+ id="p0"
203
+ marker-end="url(#arrow)"
204
+ stroke="#871f1f"
205
+ stroke-width="1"
206
+ />
207
+ <path
208
+ d="M 0,4 h 90"
209
+ fill="none"
210
+ id="path-for-text"
211
+ />
212
+ <text
213
+ font-size="8"
214
+ >
215
+ <textpath
216
+ dominant-baseline="middle"
217
+ href="#path-for-text"
218
+ startOffset="50%"
219
+ text-anchor="middle"
220
+ y="10%"
221
+ >
222
+ label_1_1, label_1_2
223
+ </textpath>
224
+ </text>
225
+ </svg>
226
+ </td>
227
+ <td
228
+ class=""
229
+ >
230
+ <div
231
+ class="ui breadcrumb"
232
+ >
233
+ <a
234
+ class="section"
235
+ href="/structures"
236
+ >
237
+ Structures
238
+ </a>
239
+ <i
240
+ aria-hidden="true"
241
+ class="right angle icon divider"
242
+ />
243
+ <a
244
+ class="section"
245
+ href="/systems/11/structures"
246
+ >
247
+ Azure Databricks
248
+ </a>
249
+ <i
250
+ aria-hidden="true"
251
+ class="right angle icon divider"
252
+ />
253
+ <a
254
+ class="section"
255
+ href="/structures/147587"
256
+ >
257
+ bdrs
258
+ </a>
259
+ <i
260
+ aria-hidden="true"
261
+ class="right angle icon divider"
262
+ />
263
+ <a
264
+ class="section"
265
+ href="/structures/151648"
266
+ >
267
+ inpahem_siniepago
268
+ </a>
269
+ <i
270
+ aria-hidden="true"
271
+ class="right angle icon divider"
272
+ />
273
+ <a
274
+ class="active section"
275
+ href="/structures/151674"
276
+ >
277
+ ivalindm
278
+ </a>
279
+ </div>
280
+ </td>
281
+ </tr>
282
+ </tbody>
283
+ </table>
284
+ </div>
285
+ </div>
286
+ </div>
287
+ `;
@@ -18,6 +18,10 @@ exports[`<StructureTabRoutes /> matches the latest snapshot 1`] = `
18
18
  component={[Function]}
19
19
  path="/structures/:id/links"
20
20
  />
21
+ <Route
22
+ component={[Function]}
23
+ path="/structures/:id/structureLinks"
24
+ />
21
25
  <Route
22
26
  component={[Function]}
23
27
  path="/structures/:id/versions"
@@ -17,6 +17,7 @@ import StructureFilters from "./StructureFilters";
17
17
  import StructureFiltersLoader from "./StructureFiltersLoader";
18
18
  import StructureItem from "./StructureItem";
19
19
  import StructureItemRoot from "./StructureItemRoot";
20
+ import StructureStructureLinks from "./StructureStructureLinks";
20
21
  import StructureLoader from "./StructureLoader";
21
22
  import StructureNav from "./StructureNav";
22
23
  import StructureNotes from "./StructureNotes";
@@ -70,6 +71,7 @@ export {
70
71
  StructureFiltersLoader,
71
72
  StructureItem,
72
73
  StructureItemRoot,
74
+ StructureStructureLinks,
73
75
  StructureLoader,
74
76
  StructureNav,
75
77
  StructureNotes,
@@ -371,6 +371,9 @@ export default {
371
371
  "structure.version.version": "Version {version}",
372
372
  "structureDomain.actions.update": "Update",
373
373
  "structureDomain.actions.update.inherit": "Apply on children",
374
+ "structureLink.currentStructure": "Current structure",
375
+ "structureLink.label": "Relation label",
376
+ "structureLink.relatedStructure": "Related structure",
374
377
  "structureSelector.union.criteria": "Union criteria",
375
378
  "structureSelector.union.type": "Union type",
376
379
  "structureTag.comment": "Comments",
@@ -551,6 +554,7 @@ export default {
551
554
  "tabs.dd.impact": "Impact",
552
555
  "tabs.dd.lineage": "Lineage",
553
556
  "tabs.dd.links": "Linkage",
557
+ "tabs.dd.structureLinks": "Linked structures",
554
558
  "tabs.dd.metadata": "Metadata",
555
559
  "tabs.dd.parents": "Parents",
556
560
  "tabs.dd.profile": "Profiling",
@@ -379,6 +379,9 @@ export default {
379
379
  "structure.version.version": "Versión {version}",
380
380
  "structureDomain.actions.update": "Actualizar",
381
381
  "structureDomain.actions.update.inherit": "Aplicar en hijos",
382
+ "structureLink.currentStructure": "Estructura actual",
383
+ "structureLink.label": "Etiqueta de relación",
384
+ "structureLink.relatedStructure": "Estructura relacionada",
382
385
  "structureSelector.union.criteria": "Criterio de unión",
383
386
  "structureSelector.union.type": "Tipo de unión",
384
387
  "structureTag.comment": "Comentario",
@@ -567,6 +570,7 @@ export default {
567
570
  "tabs.dd.impact": "Impacto",
568
571
  "tabs.dd.lineage": "Linaje",
569
572
  "tabs.dd.links": "Vinculación",
573
+ "tabs.dd.structureLinks": "Vinculación estructuras",
570
574
  "tabs.dd.metadata": "Metadata",
571
575
  "tabs.dd.parents": "Padres",
572
576
  "tabs.dd.profile": "Perfilado",
@@ -7,7 +7,7 @@ describe("reducers: structureNotes", () => {
7
7
  const initialState = {
8
8
  structureNotes: [],
9
9
  _actions: {},
10
- loading: false
10
+ loading: false,
11
11
  };
12
12
 
13
13
  it("should provide the initial state", () => {
@@ -21,6 +21,7 @@ const structureFields = (fn) =>
21
21
  const structureVersionFields = _.pick([
22
22
  "class",
23
23
  "classes",
24
+ "data_structure_link_count",
24
25
  "data_structure_type",
25
26
  "degree",
26
27
  "deleted_at",
@@ -8,6 +8,7 @@ import {
8
8
  STRUCTURE_NOTES_EDIT,
9
9
  STRUCTURE_PARENTS,
10
10
  STRUCTURE_PROFILE,
11
+ STRUCTURE_LINKED_STRUCTURES,
11
12
  STRUCTURE_RULES,
12
13
  STRUCTURE_LINKS,
13
14
  STRUCTURE_LINKS_NEW,
@@ -43,6 +44,7 @@ export const getActiveTab = (tabVisibility, path) => {
43
44
  [_.eq(STRUCTURE_METADATA), _.constant("metadata")],
44
45
  [_.eq(STRUCTURE_PARENTS), _.constant("parents")],
45
46
  [_.eq(STRUCTURE_PROFILE), _.constant("profile")],
47
+ [_.eq(STRUCTURE_LINKED_STRUCTURES), _.constant("structureLinks")],
46
48
  [_.eq(STRUCTURE_RULES), _.constant("rules")],
47
49
  [_.eq(STRUCTURE_LINKS), _.constant("links")],
48
50
  [_.eq(STRUCTURE_LINKS_NEW), _.constant("links")],
@@ -36,6 +36,8 @@ const notesTabVisible = ({ templatesLoading, templates, structure }) =>
36
36
  )(templates);
37
37
 
38
38
  const versionsTabVisible = notEmptyPath("structureVersions");
39
+ const structureLinksTabVisible = (state) =>
40
+ state?.structure?.data_structure_link_count > 0;
39
41
  const rulesTabVisible = (state) => state?.structure?.implementation_count > 0;
40
42
  const lineageTabVisible = _.anyPass([_.path("structure.degree.in")]);
41
43
  const impactTabVisible = _.anyPass([_.path("structure.degree.out")]);
@@ -48,6 +50,7 @@ export const getTabVisibility = (state) => ({
48
50
  fields: fieldsTabVisible(state),
49
51
  profile: profileTabVisible(state),
50
52
  links: linksTabVisible(state),
53
+ structureLinks: structureLinksTabVisible(state),
51
54
  notes: notesTabVisible(state),
52
55
  metadata: !_.isEmpty(metadataViewsSelector(state)),
53
56
  versions: versionsTabVisible(state),