@truedat/lm 7.5.11 → 7.5.13
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/lm",
|
|
3
|
-
"version": "7.5.
|
|
3
|
+
"version": "7.5.13",
|
|
4
4
|
"description": "Truedat Link Manager",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"module": "src/index.js",
|
|
@@ -46,35 +46,35 @@
|
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@testing-library/dom": "^10.4.0",
|
|
48
48
|
"@testing-library/jest-dom": "^6.6.3",
|
|
49
|
-
"@testing-library/react": "^16.
|
|
49
|
+
"@testing-library/react": "^16.3.0",
|
|
50
50
|
"@testing-library/user-event": "^14.6.1",
|
|
51
|
-
"@truedat/test": "7.5.
|
|
51
|
+
"@truedat/test": "7.5.13",
|
|
52
52
|
"identity-obj-proxy": "^3.0.0",
|
|
53
53
|
"jest": "^29.7.0",
|
|
54
54
|
"redux-saga-test-plan": "^4.0.6"
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@apollo/client": "^3.13.
|
|
58
|
-
"axios": "^1.
|
|
59
|
-
"graphql": "^16.
|
|
57
|
+
"@apollo/client": "^3.13.8",
|
|
58
|
+
"axios": "^1.9.0",
|
|
59
|
+
"graphql": "^16.11.0",
|
|
60
60
|
"is-hotkey": "^0.2.0",
|
|
61
61
|
"is-url": "^1.2.4",
|
|
62
62
|
"lodash": "^4.17.21",
|
|
63
63
|
"moment": "^2.30.1",
|
|
64
64
|
"path-to-regexp": "^8.2.0",
|
|
65
65
|
"prop-types": "^15.8.1",
|
|
66
|
-
"query-string": "^7.1.
|
|
67
|
-
"react": "^19.
|
|
66
|
+
"query-string": "^7.1.3",
|
|
67
|
+
"react": "^19.1.0",
|
|
68
68
|
"react-csv": "^2.2.2",
|
|
69
|
-
"react-dom": "^19.
|
|
69
|
+
"react-dom": "^19.1.0",
|
|
70
70
|
"react-dropzone": "^14.3.8",
|
|
71
71
|
"react-graph-vis": "1.0.7",
|
|
72
|
-
"react-hook-form": "^7.
|
|
73
|
-
"react-intl": "^7.1.
|
|
72
|
+
"react-hook-form": "^7.56.4",
|
|
73
|
+
"react-intl": "^7.1.11",
|
|
74
74
|
"react-moment": "^1.1.3",
|
|
75
75
|
"react-rangeslider": "^2.2.0",
|
|
76
76
|
"react-redux": "^9.2.0",
|
|
77
|
-
"react-router": "^7.
|
|
77
|
+
"react-router": "^7.6.0",
|
|
78
78
|
"redux": "^5.0.1",
|
|
79
79
|
"redux-saga": "^1.3.0",
|
|
80
80
|
"redux-saga-routines": "^3.2.3",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"semantic-ui-react": "^3.0.0-beta.2",
|
|
84
84
|
"swr": "^2.3.3"
|
|
85
85
|
},
|
|
86
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "83de799e59c0d13d4d52812070d232d24f0b5f82"
|
|
87
87
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React, { useState, useEffect } from "react";
|
|
3
|
+
import { Divider } from "semantic-ui-react";
|
|
4
|
+
import { useParams } from "react-router";
|
|
5
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
6
|
+
import { useIntl } from "react-intl";
|
|
7
|
+
import { makeTagOptionsSelector } from "@truedat/core/selectors";
|
|
8
|
+
import { HistoryBackButton } from "@truedat/core/components";
|
|
9
|
+
import { linkTo } from "@truedat/core/routes";
|
|
10
|
+
import { Button } from "semantic-ui-react";
|
|
11
|
+
|
|
12
|
+
import { createRelation, clearSelectedRelationTags } from "../routines";
|
|
13
|
+
import TagTypeDropdownSelector from "./TagTypeDropdownSelector";
|
|
14
|
+
|
|
15
|
+
const StructureSuggestions = React.lazy(() =>
|
|
16
|
+
import("@truedat/dd/components/StructureSuggestions")
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const selectTagOptions = makeTagOptionsSelector("data_field");
|
|
20
|
+
|
|
21
|
+
export const ConceptSuggestionLinkForm = () => {
|
|
22
|
+
const { business_concept_id: sourceId, id: version } = useParams();
|
|
23
|
+
const { tagOptions, selectedRelationTags } = useSelector((state) => ({
|
|
24
|
+
tagOptions: selectTagOptions(state),
|
|
25
|
+
selectedRelationTags: state.selectedRelationTags,
|
|
26
|
+
}));
|
|
27
|
+
const dispatch = useDispatch();
|
|
28
|
+
const [selectedStructure, setSelectedStructure] = useState(null);
|
|
29
|
+
const { formatMessage } = useIntl();
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
return () => {
|
|
33
|
+
dispatch(clearSelectedRelationTags.trigger());
|
|
34
|
+
};
|
|
35
|
+
}, [clearSelectedRelationTags, dispatch]);
|
|
36
|
+
|
|
37
|
+
const handleSubmit = () => {
|
|
38
|
+
const structureLink = {
|
|
39
|
+
redirectUrl: linkTo.CONCEPT_LINKS_STRUCTURES({
|
|
40
|
+
business_concept_id: sourceId,
|
|
41
|
+
id: version,
|
|
42
|
+
}),
|
|
43
|
+
source_id: sourceId,
|
|
44
|
+
source_type: "business_concept",
|
|
45
|
+
target_id: selectedStructure.id,
|
|
46
|
+
target_type: "data_structure",
|
|
47
|
+
tag_ids: selectedRelationTags ? selectedRelationTags : [],
|
|
48
|
+
};
|
|
49
|
+
dispatch(createRelation.trigger(structureLink));
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const disabled = !(selectedStructure && selectedRelationTags);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<Divider hidden />
|
|
57
|
+
{!_.isEmpty(tagOptions) && (
|
|
58
|
+
<>
|
|
59
|
+
<TagTypeDropdownSelector options={tagOptions} />
|
|
60
|
+
<Divider hidden />
|
|
61
|
+
</>
|
|
62
|
+
)}
|
|
63
|
+
<StructureSuggestions
|
|
64
|
+
handleSelectedStructure={setSelectedStructure}
|
|
65
|
+
selectedStructure={selectedStructure}
|
|
66
|
+
selectable={true}
|
|
67
|
+
/>
|
|
68
|
+
<Divider hidden />
|
|
69
|
+
<Button.Group>
|
|
70
|
+
<Button
|
|
71
|
+
primary
|
|
72
|
+
content={formatMessage({ id: "actions.create" })}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
onClick={handleSubmit}
|
|
75
|
+
/>
|
|
76
|
+
<HistoryBackButton content={formatMessage({ id: "actions.cancel" })} />
|
|
77
|
+
</Button.Group>
|
|
78
|
+
</>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default ConceptSuggestionLinkForm;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { render, fireEvent, screen, waitFor } from "@testing-library/react";
|
|
2
|
+
import ConceptSuggestionLinkForm from "../ConceptSuggestionLinkForm";
|
|
3
|
+
import { useSelector, useDispatch } from "react-redux";
|
|
4
|
+
import { useParams } from "react-router";
|
|
5
|
+
import { createRelation, clearSelectedRelationTags } from "../../routines";
|
|
6
|
+
|
|
7
|
+
jest.mock("react-redux", () => ({
|
|
8
|
+
useSelector: jest.fn(),
|
|
9
|
+
useDispatch: jest.fn(),
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
jest.mock("react-router", () => ({
|
|
13
|
+
useParams: jest.fn(),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
jest.mock("react-intl", () => ({
|
|
17
|
+
useIntl: () => ({
|
|
18
|
+
formatMessage: ({ id }) => id, // mock translated strings
|
|
19
|
+
}),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
// Mock selector factory
|
|
23
|
+
jest.mock("@truedat/core/selectors", () => ({
|
|
24
|
+
makeTagOptionsSelector: jest.fn(() => jest.fn(() => [{ id: "tag1", name: "Tag 1" }])),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
// Mock components
|
|
28
|
+
jest.mock("@truedat/dd/components/StructureSuggestions", () =>
|
|
29
|
+
jest.fn((props) => {
|
|
30
|
+
// Include a button to simulate structure selection
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<div>MockStructureSuggestions</div>
|
|
34
|
+
<button onClick={() => props.handleSelectedStructure({ id: "structure123" })}>
|
|
35
|
+
Select Structure
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
jest.mock("../TagTypeDropdownSelector", () =>
|
|
43
|
+
jest.fn(() => <div>MockTagTypeDropdownSelector</div>)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
jest.mock("@truedat/core/components", () => ({
|
|
47
|
+
HistoryBackButton: (props) => <button>{props.content}</button>,
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
jest.mock("@truedat/core/routes", () => ({
|
|
51
|
+
linkTo: {
|
|
52
|
+
CONCEPT_LINKS_STRUCTURES: jest.fn(() => "/mocked/redirect/url"),
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
describe("ConceptSuggestionLinkForm", () => {
|
|
57
|
+
const mockDispatch = jest.fn();
|
|
58
|
+
|
|
59
|
+
beforeEach(() => {
|
|
60
|
+
jest.clearAllMocks();
|
|
61
|
+
|
|
62
|
+
useParams.mockReturnValue({ business_concept_id: "bc1", id: "v1" });
|
|
63
|
+
useDispatch.mockReturnValue(mockDispatch);
|
|
64
|
+
useSelector.mockImplementation((selector) =>
|
|
65
|
+
selector({
|
|
66
|
+
selectedRelationTags: ["tag1"],
|
|
67
|
+
})
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
clearSelectedRelationTags.trigger = jest.fn(() => ({ type: "CLEAR_TAGS" }));
|
|
71
|
+
createRelation.trigger = jest.fn((payload) => ({ type: "CREATE_RELATION", payload }));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("renders structure suggestions and tag dropdown", async () => {
|
|
75
|
+
render(<ConceptSuggestionLinkForm />);
|
|
76
|
+
|
|
77
|
+
await waitFor(() => {
|
|
78
|
+
expect(screen.getByText("MockStructureSuggestions")).toBeInTheDocument();
|
|
79
|
+
expect(screen.getByText("actions.create")).toBeDisabled();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("dispatches createRelation on submit", async () => {
|
|
85
|
+
render(<ConceptSuggestionLinkForm />);
|
|
86
|
+
|
|
87
|
+
// Simulate structure selection
|
|
88
|
+
fireEvent.click(screen.getByText("Select Structure"));
|
|
89
|
+
|
|
90
|
+
// Now the submit button should be enabled
|
|
91
|
+
const createBtn = screen.getByText("actions.create");
|
|
92
|
+
expect(createBtn).not.toBeDisabled();
|
|
93
|
+
|
|
94
|
+
fireEvent.click(createBtn);
|
|
95
|
+
|
|
96
|
+
expect(createRelation.trigger).toHaveBeenCalledWith({
|
|
97
|
+
redirectUrl: "/mocked/redirect/url",
|
|
98
|
+
source_id: "bc1",
|
|
99
|
+
source_type: "business_concept",
|
|
100
|
+
target_id: "structure123",
|
|
101
|
+
target_type: "data_structure",
|
|
102
|
+
tag_ids: ["tag1"],
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(mockDispatch).toHaveBeenCalledWith(expect.objectContaining({ type: "CREATE_RELATION" }));
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("dispatches clearSelectedRelationTags on unmount", () => {
|
|
109
|
+
const { unmount } = render(<ConceptSuggestionLinkForm />);
|
|
110
|
+
unmount();
|
|
111
|
+
|
|
112
|
+
expect(clearSelectedRelationTags.trigger).toHaveBeenCalled();
|
|
113
|
+
expect(mockDispatch).toHaveBeenCalledWith({ type: "CLEAR_TAGS" });
|
|
114
|
+
});
|
|
115
|
+
});
|