@truedat/dd 8.4.7 → 8.4.9
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 -3
- package/src/components/LineageGraph.js +100 -9
- package/src/components/StructureGraph.js +3 -1
- package/src/components/StructureLineage.js +10 -13
- package/src/components/StructureTabs.js +1 -1
- package/src/components/StructureVersions.js +6 -2
- package/src/messages/en.js +4 -0
- package/src/messages/es.js +4 -0
- package/src/selectors/getLineageLevels.js +1 -4
- package/src/styles/lineageGraph.less +65 -0
- package/src/components/StructureSuggestions.js +0 -88
- package/src/components/__tests__/StructureSuggestions.spec.js +0 -93
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "8.4.
|
|
3
|
+
"version": "8.4.9",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"@testing-library/jest-dom": "^6.6.3",
|
|
52
52
|
"@testing-library/react": "^16.3.0",
|
|
53
53
|
"@testing-library/user-event": "^14.6.1",
|
|
54
|
-
"@truedat/test": "8.4.
|
|
54
|
+
"@truedat/test": "8.4.9",
|
|
55
55
|
"identity-obj-proxy": "^3.0.0",
|
|
56
56
|
"jest": "^29.7.0",
|
|
57
57
|
"redux-saga-test-plan": "^4.0.6"
|
|
@@ -86,5 +86,5 @@
|
|
|
86
86
|
"svg-pan-zoom": "^3.6.2",
|
|
87
87
|
"swr": "^2.3.3"
|
|
88
88
|
},
|
|
89
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "9fd4bd2126a33342009194c8ae1829cd3c617a48"
|
|
90
90
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import { useRef, useEffect, useReducer, useState } from "react";
|
|
2
|
+
import { useRef, useEffect, useReducer, useState, useCallback } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
5
|
import {
|
|
@@ -39,9 +39,6 @@ const enablePanZoom = (svg) => {
|
|
|
39
39
|
const togglePanZoom = (svg) => (selectedNode) => {
|
|
40
40
|
_.isEmpty(selectedNode) ? enablePanZoom(svg) : disablePanZoom(svg);
|
|
41
41
|
};
|
|
42
|
-
const resetPan = (svg) => {
|
|
43
|
-
svg && svg.resetPan();
|
|
44
|
-
};
|
|
45
42
|
|
|
46
43
|
const initialState = {
|
|
47
44
|
hover: {},
|
|
@@ -70,29 +67,120 @@ export const LineageGraph = ({
|
|
|
70
67
|
cancelPoll,
|
|
71
68
|
}) => {
|
|
72
69
|
const ref = useRef(null);
|
|
70
|
+
const canvasRef = useRef(null);
|
|
71
|
+
const fitFrameRef = useRef(null);
|
|
72
|
+
const isAliveRef = useRef(true);
|
|
73
73
|
const [state, dispatch] = useReducer(reducer, initialState);
|
|
74
74
|
const { selectedNode, hover, svg } = state;
|
|
75
75
|
|
|
76
76
|
const [metadata, setMetadata] = useState(null);
|
|
77
77
|
const [selectedLink, selectLink] = useState(null);
|
|
78
78
|
|
|
79
|
+
const fitAndCenterGraph = useCallback((instance) => {
|
|
80
|
+
if (!instance || !isAliveRef.current) return;
|
|
81
|
+
if (fitFrameRef.current) {
|
|
82
|
+
cancelAnimationFrame(fitFrameRef.current);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fitFrameRef.current = requestAnimationFrame(() => {
|
|
86
|
+
if (!isAliveRef.current) {
|
|
87
|
+
fitFrameRef.current = null;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const initialScale = 0.8;
|
|
92
|
+
const topMargin = 16;
|
|
93
|
+
instance.resize();
|
|
94
|
+
instance.fit();
|
|
95
|
+
instance.zoomBy(initialScale);
|
|
96
|
+
const currentPan = instance.getPan();
|
|
97
|
+
instance.pan({ x: currentPan.x, y: topMargin });
|
|
98
|
+
} catch (_error) {
|
|
99
|
+
} finally {
|
|
100
|
+
fitFrameRef.current = null;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}, []);
|
|
104
|
+
|
|
79
105
|
useEffect(() => {
|
|
80
106
|
// Anything in here is fired on component mount.
|
|
81
|
-
return () =>
|
|
107
|
+
return () => {
|
|
108
|
+
isAliveRef.current = false;
|
|
109
|
+
cancelPoll();
|
|
110
|
+
};
|
|
82
111
|
}, []);
|
|
83
112
|
|
|
84
113
|
useEffect(() => {
|
|
85
|
-
if (!_.isEmpty(resources)) {
|
|
86
|
-
|
|
114
|
+
if (!_.isEmpty(resources) && ref.current) {
|
|
115
|
+
isAliveRef.current = true;
|
|
116
|
+
const svg = svgPanZoom(ref.current, {
|
|
117
|
+
...svgOptions,
|
|
118
|
+
eventsListenerElement: canvasRef.current || ref.current,
|
|
119
|
+
});
|
|
87
120
|
dispatch(initSvg.trigger(svg));
|
|
121
|
+
|
|
122
|
+
return () => {
|
|
123
|
+
isAliveRef.current = false;
|
|
124
|
+
if (fitFrameRef.current) {
|
|
125
|
+
cancelAnimationFrame(fitFrameRef.current);
|
|
126
|
+
fitFrameRef.current = null;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
svg.destroy();
|
|
130
|
+
} catch (_error) {
|
|
131
|
+
}
|
|
132
|
+
};
|
|
88
133
|
}
|
|
89
134
|
}, [resources]);
|
|
90
135
|
useEffect(() => togglePanZoom(svg)(selectedNode), [selectedNode]);
|
|
91
|
-
useEffect(() =>
|
|
136
|
+
useEffect(() => fitAndCenterGraph(svg), [svg, resources]);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
if (!svg) return;
|
|
139
|
+
|
|
140
|
+
let isResizeEffectActive = true;
|
|
141
|
+
const onResize = () => {
|
|
142
|
+
if (!isResizeEffectActive || !isAliveRef.current) return;
|
|
143
|
+
fitAndCenterGraph(svg);
|
|
144
|
+
};
|
|
145
|
+
window.addEventListener("resize", onResize);
|
|
146
|
+
|
|
147
|
+
const resizeObserver =
|
|
148
|
+
typeof ResizeObserver !== "undefined" && canvasRef.current
|
|
149
|
+
? new ResizeObserver(onResize)
|
|
150
|
+
: null;
|
|
151
|
+
|
|
152
|
+
if (resizeObserver && canvasRef.current) {
|
|
153
|
+
resizeObserver.observe(canvasRef.current);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return () => {
|
|
157
|
+
isResizeEffectActive = false;
|
|
158
|
+
window.removeEventListener("resize", onResize);
|
|
159
|
+
if (resizeObserver) resizeObserver.disconnect();
|
|
160
|
+
if (fitFrameRef.current) {
|
|
161
|
+
cancelAnimationFrame(fitFrameRef.current);
|
|
162
|
+
fitFrameRef.current = null;
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}, [svg]);
|
|
92
166
|
|
|
93
167
|
const onSelect = (g) => () => dispatch(selectNode.trigger(g));
|
|
94
168
|
const onHover = (g) =>
|
|
95
169
|
selectedNode ? undefined : () => dispatch(hoverNode.trigger(g));
|
|
170
|
+
const onPointerDown = (event) => {
|
|
171
|
+
if (event.currentTarget.setPointerCapture && event.pointerId !== undefined) {
|
|
172
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const onPointerUp = (event) => {
|
|
177
|
+
if (
|
|
178
|
+
event.currentTarget.releasePointerCapture &&
|
|
179
|
+
event.pointerId !== undefined
|
|
180
|
+
) {
|
|
181
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
96
184
|
const { formatMessage } = useIntl();
|
|
97
185
|
|
|
98
186
|
return (
|
|
@@ -104,7 +192,7 @@ export const LineageGraph = ({
|
|
|
104
192
|
_.isNull(metadata) || _.isEmpty(metadata) ? {} : { paddingRight: 0 }
|
|
105
193
|
}
|
|
106
194
|
>
|
|
107
|
-
<Dimmer.Dimmable dimmed={loading} className="dl">
|
|
195
|
+
<Dimmer.Dimmable dimmed={loading} className="dl" ref={canvasRef}>
|
|
108
196
|
{loading ? (
|
|
109
197
|
<Dimmer active inverted>
|
|
110
198
|
<Loader size="large">
|
|
@@ -149,6 +237,9 @@ export const LineageGraph = ({
|
|
|
149
237
|
height={height}
|
|
150
238
|
ref={ref}
|
|
151
239
|
onClick={() => dispatch(selectNode.trigger(null))}
|
|
240
|
+
onPointerDown={onPointerDown}
|
|
241
|
+
onPointerUp={onPointerUp}
|
|
242
|
+
onPointerCancel={onPointerUp}
|
|
152
243
|
style={{ border: "1px solid #d4d4d5", borderRadius: "5px" }}
|
|
153
244
|
>
|
|
154
245
|
<LineageDefs />
|
|
@@ -13,6 +13,7 @@ export const StructureGraph = ({
|
|
|
13
13
|
graph,
|
|
14
14
|
id,
|
|
15
15
|
graphId,
|
|
16
|
+
height,
|
|
16
17
|
type,
|
|
17
18
|
}) => {
|
|
18
19
|
useEffect(() => {
|
|
@@ -24,7 +25,7 @@ export const StructureGraph = ({
|
|
|
24
25
|
return degree ? (
|
|
25
26
|
<>
|
|
26
27
|
<GraphRedirector />
|
|
27
|
-
<LineageGraph height="
|
|
28
|
+
<LineageGraph height={height || "640px"} graph={graph} />
|
|
28
29
|
</>
|
|
29
30
|
) : null;
|
|
30
31
|
};
|
|
@@ -36,6 +37,7 @@ StructureGraph.propTypes = {
|
|
|
36
37
|
graph: PropTypes.object,
|
|
37
38
|
id: PropTypes.string,
|
|
38
39
|
graphId: PropTypes.number,
|
|
40
|
+
height: PropTypes.string,
|
|
39
41
|
type: PropTypes.string,
|
|
40
42
|
};
|
|
41
43
|
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
import { connect } from "react-redux";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
-
import { Grid } from "semantic-ui-react";
|
|
4
3
|
import StructureGraph from "./StructureGraph";
|
|
5
4
|
import LineageDownloadButton from "./LineageDownloadButton";
|
|
6
5
|
|
|
7
|
-
export const StructureLineage = (props) =>
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
</>
|
|
18
|
-
);
|
|
6
|
+
export const StructureLineage = (props) => {
|
|
7
|
+
return (
|
|
8
|
+
<div className="structure-lineage">
|
|
9
|
+
<div className="structure-lineage__download-button">
|
|
10
|
+
<LineageDownloadButton graph={props.graph} />
|
|
11
|
+
</div>
|
|
12
|
+
<StructureGraph {...props} height="100%" />
|
|
13
|
+
</div>
|
|
14
|
+
);
|
|
15
|
+
};
|
|
19
16
|
|
|
20
17
|
const mapStateToProps = ({
|
|
21
18
|
structureLineageId: graphId,
|
|
@@ -19,8 +19,12 @@ export const StructureVersions = () =>{
|
|
|
19
19
|
return (
|
|
20
20
|
!_.isEmpty(versions) && (
|
|
21
21
|
<List selection>
|
|
22
|
-
{versions.map((v
|
|
23
|
-
<StructureVersionListItem
|
|
22
|
+
{versions.map((v) => (
|
|
23
|
+
<StructureVersionListItem
|
|
24
|
+
key={v?.id ?? `${v?.version}-${v?.inserted_at}`}
|
|
25
|
+
id={id}
|
|
26
|
+
{...v}
|
|
27
|
+
/>
|
|
24
28
|
))}
|
|
25
29
|
</List>
|
|
26
30
|
));
|
package/src/messages/en.js
CHANGED
|
@@ -397,7 +397,11 @@ export default {
|
|
|
397
397
|
"structure.rule.props.name": "Name",
|
|
398
398
|
"structure.rule_implementation.props.key": "Implementation",
|
|
399
399
|
"structure.rule_implementation.props.result": "Quality",
|
|
400
|
+
"structure.reason": "Reason",
|
|
400
401
|
"structure.search.placeholder": "Enter a search...",
|
|
402
|
+
"structure.similarity": "Similarity",
|
|
403
|
+
"suggestions.similarity.cosine.popup":
|
|
404
|
+
"Similarity score (cosine). Higher values indicate a closer match.",
|
|
401
405
|
"structure.share.title": "Share structure",
|
|
402
406
|
"structure.system": "System",
|
|
403
407
|
"structure.system.create": "Create system",
|
package/src/messages/es.js
CHANGED
|
@@ -407,7 +407,11 @@ export default {
|
|
|
407
407
|
"structure.rule.props.name": "Nombre",
|
|
408
408
|
"structure.rule_implementation.props.key": "Implementación",
|
|
409
409
|
"structure.rule_implementation.props.result": "Calidad",
|
|
410
|
+
"structure.reason": "Motivo",
|
|
410
411
|
"structure.search.placeholder": "Introduce una busqueda...",
|
|
412
|
+
"structure.similarity": "Similitud",
|
|
413
|
+
"suggestions.similarity.cosine.popup":
|
|
414
|
+
"Puntuación de similitud (coseno). Valores más altos indican mayor coincidencia.",
|
|
411
415
|
"structure.share.title": "Compartir estructura",
|
|
412
416
|
"structure.system": "Sistema",
|
|
413
417
|
"structure.system.create": "Crear sistema",
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import { createSelector } from "reselect";
|
|
3
3
|
|
|
4
|
-
export const lineageLevels =
|
|
5
|
-
_.prop("lineageLevels"),
|
|
6
|
-
(lineageLevels) => lineageLevels
|
|
7
|
-
);
|
|
4
|
+
export const lineageLevels = _.prop("lineageLevels");
|
|
8
5
|
|
|
9
6
|
export const levelsSelection = createSelector(lineageLevels, (lineageLevels) =>
|
|
10
7
|
_.negate(_.isNil)(lineageLevels) ? "select" : "all"
|
|
@@ -1,3 +1,68 @@
|
|
|
1
1
|
.graph-hash {
|
|
2
2
|
font-family: monospace;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.structure-lineage {
|
|
6
|
+
position: relative;
|
|
7
|
+
width: 100%;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
margin-bottom: 0;
|
|
10
|
+
padding-bottom: 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.structure-lineage__download-button {
|
|
14
|
+
position: absolute;
|
|
15
|
+
top: 4%;
|
|
16
|
+
right: 1%;
|
|
17
|
+
z-index: 5;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.structure-lineage__download-button span,
|
|
21
|
+
.structure-lineage__download-button .ui.button,
|
|
22
|
+
.structure-lineage__download-button .ui.popup {
|
|
23
|
+
pointer-events: auto;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.structure-lineage .ui.grid {
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding-bottom: 0;
|
|
29
|
+
padding-left: 0;
|
|
30
|
+
padding-right: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.structure-lineage .ui.grid > .row {
|
|
34
|
+
padding-top: 0;
|
|
35
|
+
padding-bottom: 0;
|
|
36
|
+
margin-bottom: 0;
|
|
37
|
+
margin-left: 0;
|
|
38
|
+
margin-right: 0;
|
|
39
|
+
padding-left: 0;
|
|
40
|
+
padding-right: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.structure-lineage .ui.grid > * {
|
|
44
|
+
padding-left: 0;
|
|
45
|
+
padding-right: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.structure-lineage .ui.grid > .row > .column {
|
|
49
|
+
padding-bottom: 0;
|
|
50
|
+
padding-left: 0;
|
|
51
|
+
padding-right: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.structure-lineage .dl {
|
|
55
|
+
padding: 0;
|
|
56
|
+
margin: 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.structure-lineage .dl svg {
|
|
60
|
+
height: clamp(340px, 52vw, 640px) !important;
|
|
61
|
+
max-height: 640px;
|
|
62
|
+
background-color: #f7f8fa;
|
|
63
|
+
padding: 0;
|
|
64
|
+
margin: 0;
|
|
65
|
+
display: block;
|
|
66
|
+
margin-left: 0;
|
|
67
|
+
margin-right: 0;
|
|
3
68
|
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import _ from "lodash/fp";
|
|
2
|
-
import React, { useEffect } from "react";
|
|
3
|
-
import { Popup } from "semantic-ui-react";
|
|
4
|
-
import { useIntl } from "react-intl";
|
|
5
|
-
import { useParams } from "react-router";
|
|
6
|
-
import { useSelector } from "react-redux";
|
|
7
|
-
import PropTypes from "prop-types";
|
|
8
|
-
import { useDataStructureSuggestions } from "../hooks/useStructures";
|
|
9
|
-
import { defaultColumnsForStructureSelector } from "../selectors";
|
|
10
|
-
import StructuresSearchResults from "./StructuresSearchResults";
|
|
11
|
-
|
|
12
|
-
const SimilarityColumn = (similarity) => {
|
|
13
|
-
const { formatMessage } = useIntl();
|
|
14
|
-
return (
|
|
15
|
-
<Popup
|
|
16
|
-
content={formatMessage({ id: "suggestions.similarity.cosine.popup" })}
|
|
17
|
-
trigger={<span>{similarity.toFixed(3)}</span>}
|
|
18
|
-
/>
|
|
19
|
-
);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const similarityColumnDefinition = {
|
|
23
|
-
name: "similarity",
|
|
24
|
-
fieldSelector: _.prop("similarity"),
|
|
25
|
-
fieldDecorator: SimilarityColumn,
|
|
26
|
-
width: 1,
|
|
27
|
-
};
|
|
28
|
-
const suggestionColumns = (state) =>
|
|
29
|
-
_.concat(defaultColumnsForStructureSelector(state))([
|
|
30
|
-
similarityColumnDefinition,
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
export const StructureSuggestions = ({
|
|
34
|
-
selectedStructure,
|
|
35
|
-
handleSelectedStructure,
|
|
36
|
-
selectable,
|
|
37
|
-
}) => {
|
|
38
|
-
const { business_concept_id: id, id: version } = useParams();
|
|
39
|
-
const { conceptLinks, columns } = useSelector((state) => ({
|
|
40
|
-
conceptLinks: state.conceptLinks,
|
|
41
|
-
columns: suggestionColumns(state),
|
|
42
|
-
}));
|
|
43
|
-
|
|
44
|
-
const {
|
|
45
|
-
trigger: fetchSuggestions,
|
|
46
|
-
data,
|
|
47
|
-
isMutating,
|
|
48
|
-
} = useDataStructureSuggestions();
|
|
49
|
-
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (id && version && conceptLinks) {
|
|
52
|
-
fetchSuggestions({
|
|
53
|
-
resource: {
|
|
54
|
-
id,
|
|
55
|
-
version,
|
|
56
|
-
type: "concepts",
|
|
57
|
-
links: _.filter(
|
|
58
|
-
({ resource_type }) => resource_type == "data_structure"
|
|
59
|
-
)(conceptLinks),
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}, [id, version, conceptLinks]);
|
|
64
|
-
|
|
65
|
-
const structures = data?.data?.data || [];
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<StructuresSearchResults
|
|
69
|
-
loading={isMutating}
|
|
70
|
-
columns={columns}
|
|
71
|
-
size="small"
|
|
72
|
-
structures={structures}
|
|
73
|
-
selectedStructure={selectedStructure}
|
|
74
|
-
onSelect={handleSelectedStructure}
|
|
75
|
-
labelResults={false}
|
|
76
|
-
pagination={false}
|
|
77
|
-
selectable={selectable}
|
|
78
|
-
/>
|
|
79
|
-
);
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
StructureSuggestions.propTypes = {
|
|
83
|
-
handleSelectedStructure: PropTypes.func,
|
|
84
|
-
selectedStructure: PropTypes.object,
|
|
85
|
-
selectable: PropTypes.bool,
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export default StructureSuggestions;
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { render, screen } from "@testing-library/react";
|
|
3
|
-
import { useSelector } from "react-redux";
|
|
4
|
-
import { useParams } from "react-router";
|
|
5
|
-
import { useDataStructureSuggestions } from "../../hooks/useStructures";
|
|
6
|
-
import StructureSuggestions from "../StructureSuggestions";
|
|
7
|
-
import StructuresSearchResults from "../StructuresSearchResults";
|
|
8
|
-
import * as selectors from "../../selectors";
|
|
9
|
-
|
|
10
|
-
jest.mock("react-router", () => ({
|
|
11
|
-
useParams: jest.fn(),
|
|
12
|
-
}));
|
|
13
|
-
|
|
14
|
-
jest.mock("react-redux", () => ({
|
|
15
|
-
useSelector: jest.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
jest.mock("../../hooks/useStructures", () => ({
|
|
19
|
-
useDataStructureSuggestions: jest.fn(),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
jest.mock("../StructuresSearchResults", () => jest.fn(() => <div>MockSearchResults</div>));
|
|
23
|
-
|
|
24
|
-
jest.mock("../../selectors", () => ({
|
|
25
|
-
defaultColumnsForStructureSelector: jest.fn(),
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
|
-
describe("StructureSuggestions", () => {
|
|
29
|
-
const mockFetchSuggestions = jest.fn();
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
jest.clearAllMocks();
|
|
33
|
-
|
|
34
|
-
selectors.defaultColumnsForStructureSelector.mockReturnValue([
|
|
35
|
-
{ name: "name", fieldSelector: (x) => x.name },
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
|
-
useParams.mockReturnValue({ business_concept_id: "123", id: "456" });
|
|
39
|
-
|
|
40
|
-
useSelector.mockImplementation((selectorFn) =>
|
|
41
|
-
selectorFn({
|
|
42
|
-
conceptLinks: [
|
|
43
|
-
{ resource_type: "data_structure", id: "ds1" },
|
|
44
|
-
{ resource_type: "other", id: "not-ds" },
|
|
45
|
-
],
|
|
46
|
-
// mock of defaultColumnsForStructureSelector + similarity column
|
|
47
|
-
columns: [
|
|
48
|
-
{ name: "name", fieldSelector: (x) => x.name },
|
|
49
|
-
{ name: "similarity", fieldSelector: (x) => x.similarity },
|
|
50
|
-
],
|
|
51
|
-
})
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
useDataStructureSuggestions.mockReturnValue({
|
|
55
|
-
trigger: mockFetchSuggestions,
|
|
56
|
-
data: {
|
|
57
|
-
data: {
|
|
58
|
-
data: [
|
|
59
|
-
{ id: "structure1", similarity: 0.89 },
|
|
60
|
-
{ id: "structure2", similarity: 0.77 },
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
},
|
|
64
|
-
isMutating: false,
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("calls fetchSuggestions on mount with correct parameters", () => {
|
|
69
|
-
render(<StructureSuggestions selectedStructure={null} handleSelectedStructure={jest.fn()} selectable={true} />);
|
|
70
|
-
|
|
71
|
-
expect(mockFetchSuggestions).toHaveBeenCalledWith({
|
|
72
|
-
resource: {
|
|
73
|
-
id: "123",
|
|
74
|
-
version: "456",
|
|
75
|
-
type: "concepts",
|
|
76
|
-
links: [{ resource_type: "data_structure", id: "ds1" }],
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it("renders StructuresSearchResults with props", () => {
|
|
82
|
-
render(<StructureSuggestions selectedStructure={{}} handleSelectedStructure={jest.fn()} selectable={false} />);
|
|
83
|
-
|
|
84
|
-
expect(screen.getByText("MockSearchResults")).toBeInTheDocument();
|
|
85
|
-
const props = StructuresSearchResults.mock.calls[0][0];
|
|
86
|
-
expect(props.structures).toEqual([
|
|
87
|
-
{ id: "structure1", similarity: 0.89 },
|
|
88
|
-
{ id: "structure2", similarity: 0.77 },
|
|
89
|
-
]);
|
|
90
|
-
expect(props.loading).toBe(false);
|
|
91
|
-
expect(props.selectable).toBe(false);
|
|
92
|
-
});
|
|
93
|
-
});
|