@truedat/bg 5.8.1 → 5.8.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/package.json +6 -6
- package/src/concepts/components/ConceptLinksDropdownSelector.js +55 -0
- package/src/concepts/components/ConceptsLinksManagement.js +201 -97
- package/src/concepts/components/__tests__/ConceptLinksManagement.spec.js +0 -1
- package/src/concepts/components/__tests__/__snapshots__/ConceptLinksManagement.spec.js.snap +65 -1
- package/src/concepts/relations/components/ConceptRelationForm.js +55 -91
- package/src/concepts/relations/components/ConceptSelector.js +28 -1
- package/src/concepts/relations/components/__tests__/ConceptRelationForm.spec.js +103 -64
- package/src/concepts/relations/components/__tests__/__snapshots__/ConceptRelationForm.spec.js.snap +189 -51
- package/src/messages/en.js +6 -3
- package/src/messages/es.js +6 -3
|
@@ -1,49 +1,44 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useState } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { Button, Form, Checkbox } from "semantic-ui-react";
|
|
8
|
-
import { FormattedMessage } from "react-intl";
|
|
5
|
+
import { FormattedMessage, useIntl } from "react-intl";
|
|
6
|
+
import { Button, Form } from "semantic-ui-react";
|
|
9
7
|
import { HistoryBackButton } from "@truedat/core/components";
|
|
10
8
|
import { linkTo } from "@truedat/core/routes";
|
|
11
9
|
import { makeTagOptionsSelector } from "@truedat/core/selectors";
|
|
12
10
|
import { linkConcept } from "../routines";
|
|
13
11
|
import ConceptSelector from "./ConceptSelector";
|
|
14
12
|
|
|
13
|
+
const TagTypeDropdownSelector = React.lazy(() =>
|
|
14
|
+
import("@truedat/lm/components/TagTypeDropdownSelector")
|
|
15
|
+
);
|
|
16
|
+
|
|
15
17
|
const filters = {
|
|
16
18
|
current: [true],
|
|
17
19
|
status: ["pending_approval", "draft", "rejected", "published"],
|
|
18
20
|
};
|
|
19
21
|
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const { selectedTag } = this.state;
|
|
22
|
+
export const pickConceptAttrs = _.pick([
|
|
23
|
+
"name",
|
|
24
|
+
"business_concept_id",
|
|
25
|
+
"id",
|
|
26
|
+
"version",
|
|
27
|
+
]);
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
export const ConceptRelationForm = ({
|
|
30
|
+
concept,
|
|
31
|
+
tagOptions,
|
|
32
|
+
linkConcept,
|
|
33
|
+
selectedRelationTags,
|
|
34
|
+
}) => {
|
|
35
|
+
const { formatMessage } = useIntl();
|
|
36
|
+
const [selectedConcept, setSelectedConcept] = useState(null);
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
handleConceptSelected = (selectedConcept) =>
|
|
36
|
-
this.setState({ selectedConcept });
|
|
37
|
-
handleSubmit = () => {
|
|
38
|
-
const pickConceptAttrs = _.pick([
|
|
39
|
-
"name",
|
|
40
|
-
"business_concept_id",
|
|
41
|
-
"id",
|
|
42
|
-
"version",
|
|
43
|
-
]);
|
|
44
|
-
const { selectedConcept, selectedTag } = this.state;
|
|
45
|
-
const { concept } = this.props;
|
|
38
|
+
const handleConceptSelected = (selectedConcept) =>
|
|
39
|
+
setSelectedConcept(selectedConcept);
|
|
46
40
|
|
|
41
|
+
const handleSubmit = () => {
|
|
47
42
|
const redirectUrl = linkTo.CONCEPT_LINKS_CONCEPTS(concept);
|
|
48
43
|
|
|
49
44
|
const conceptLink = {
|
|
@@ -52,77 +47,46 @@ export class ConceptRelationForm extends React.Component {
|
|
|
52
47
|
source_type: "business_concept",
|
|
53
48
|
target_id: selectedConcept.business_concept_id,
|
|
54
49
|
target_type: "business_concept",
|
|
55
|
-
tag_ids:
|
|
50
|
+
tag_ids: selectedRelationTags ? selectedRelationTags : [],
|
|
56
51
|
context: {
|
|
57
52
|
target: pickConceptAttrs(selectedConcept),
|
|
58
53
|
source: pickConceptAttrs(concept),
|
|
59
54
|
},
|
|
60
55
|
};
|
|
61
|
-
|
|
56
|
+
linkConcept(conceptLink);
|
|
62
57
|
};
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
tagOptions
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
toggle
|
|
84
|
-
label={formatMessage({
|
|
85
|
-
id: `source.${tagOption.text}`,
|
|
86
|
-
defaultMessage: formatMessage({
|
|
87
|
-
id: tagOption.text,
|
|
88
|
-
defaultMessage: tagOption.text,
|
|
89
|
-
}),
|
|
90
|
-
})}
|
|
91
|
-
name={tagOption.text}
|
|
92
|
-
value={tagOption.value}
|
|
93
|
-
onChange={this.handleTagOnChange}
|
|
94
|
-
/>
|
|
95
|
-
</div>
|
|
96
|
-
))}
|
|
97
|
-
</Form.Field>
|
|
98
|
-
)}
|
|
99
|
-
<ConceptSelector
|
|
100
|
-
selectedConcept={selectedConcept}
|
|
101
|
-
handleConceptSelected={this.handleConceptSelected}
|
|
102
|
-
businessConceptId={business_concept_id}
|
|
103
|
-
defaultFilters={filters}
|
|
59
|
+
return (
|
|
60
|
+
<>
|
|
61
|
+
{!_.isEmpty(tagOptions) ? (
|
|
62
|
+
<Form.Field className="concept-relation-form">
|
|
63
|
+
<TagTypeDropdownSelector options={tagOptions} />
|
|
64
|
+
</Form.Field>
|
|
65
|
+
) : null}
|
|
66
|
+
<ConceptSelector
|
|
67
|
+
selectedConcept={selectedConcept}
|
|
68
|
+
handleConceptSelected={handleConceptSelected}
|
|
69
|
+
businessConceptId={concept.business_concept_id}
|
|
70
|
+
defaultFilters={filters}
|
|
71
|
+
/>
|
|
72
|
+
<Button.Group>
|
|
73
|
+
<Button
|
|
74
|
+
primary
|
|
75
|
+
content={<FormattedMessage id="actions.create" />}
|
|
76
|
+
disabled={!selectedConcept}
|
|
77
|
+
onClick={handleSubmit}
|
|
104
78
|
/>
|
|
105
|
-
<
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
onClick={this.handleSubmit}
|
|
111
|
-
/>
|
|
112
|
-
<HistoryBackButton
|
|
113
|
-
content={formatMessage({ id: "actions.cancel" })}
|
|
114
|
-
/>
|
|
115
|
-
</Button.Group>
|
|
116
|
-
</>
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
79
|
+
<HistoryBackButton content={formatMessage({ id: "actions.cancel" })} />
|
|
80
|
+
</Button.Group>
|
|
81
|
+
</>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
120
84
|
|
|
121
85
|
ConceptRelationForm.propTypes = {
|
|
122
86
|
tagOptions: PropTypes.array,
|
|
123
87
|
concept: PropTypes.object,
|
|
124
|
-
intl: PropTypes.object,
|
|
125
88
|
linkConcept: PropTypes.func,
|
|
89
|
+
selectedRelationTags: PropTypes.array,
|
|
126
90
|
};
|
|
127
91
|
|
|
128
92
|
const makeMapStateToProps = () => {
|
|
@@ -130,11 +94,11 @@ const makeMapStateToProps = () => {
|
|
|
130
94
|
const mapStateToProps = (state) => ({
|
|
131
95
|
tagOptions: getTagOptions(state),
|
|
132
96
|
concept: state.concept,
|
|
97
|
+
selectedRelationTags: state.selectedRelationTags,
|
|
133
98
|
});
|
|
134
99
|
return mapStateToProps;
|
|
135
100
|
};
|
|
136
101
|
|
|
137
|
-
export default
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
)(ConceptRelationForm);
|
|
102
|
+
export default connect(makeMapStateToProps, { linkConcept })(
|
|
103
|
+
ConceptRelationForm
|
|
104
|
+
);
|
|
@@ -17,13 +17,32 @@ const filters = {
|
|
|
17
17
|
status: ["pending_approval", "draft", "rejected", "published"],
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const isPositive = (links, id, selectedId) => {
|
|
21
|
+
const link =
|
|
22
|
+
id === selectedId
|
|
23
|
+
? false
|
|
24
|
+
: _.find(
|
|
25
|
+
(link) =>
|
|
26
|
+
_.propEq("target_id", id)(link) &&
|
|
27
|
+
_.propEq("target_type", "business_concept")(link)
|
|
28
|
+
)(links);
|
|
29
|
+
return link ? true : false;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const ConceptSelectorRow = ({
|
|
33
|
+
concept,
|
|
34
|
+
active,
|
|
35
|
+
onClick,
|
|
36
|
+
disabled,
|
|
37
|
+
positive,
|
|
38
|
+
}) => {
|
|
21
39
|
const { name, status, domain } = concept;
|
|
22
40
|
return (
|
|
23
41
|
<Table.Row
|
|
24
42
|
disabled={disabled}
|
|
25
43
|
active={active}
|
|
26
44
|
onClick={() => onClick && onClick(concept)}
|
|
45
|
+
positive={positive}
|
|
27
46
|
>
|
|
28
47
|
<Table.Cell content={name} />
|
|
29
48
|
<Table.Cell content={domain?.name} />
|
|
@@ -48,6 +67,7 @@ ConceptSelectorRow.propTypes = {
|
|
|
48
67
|
active: PropTypes.bool,
|
|
49
68
|
onClick: PropTypes.func,
|
|
50
69
|
disabled: PropTypes.bool,
|
|
70
|
+
positive: PropTypes.bool,
|
|
51
71
|
};
|
|
52
72
|
|
|
53
73
|
export const ConceptSelector = ({
|
|
@@ -59,6 +79,7 @@ export const ConceptSelector = ({
|
|
|
59
79
|
pageSize = 7,
|
|
60
80
|
selectedConcept,
|
|
61
81
|
showTitle = true,
|
|
82
|
+
links,
|
|
62
83
|
}) => {
|
|
63
84
|
const selectedConceptIsFiltered = !_.reduce(
|
|
64
85
|
(acc, c) => (selectedConcept && c.id == selectedConcept.id) || acc,
|
|
@@ -114,6 +135,11 @@ export const ConceptSelector = ({
|
|
|
114
135
|
concept={s}
|
|
115
136
|
active={selectedConcept && selectedConcept.id == s.id}
|
|
116
137
|
onClick={handleConceptSelected}
|
|
138
|
+
positive={isPositive(
|
|
139
|
+
links,
|
|
140
|
+
s.business_concept_id,
|
|
141
|
+
businessConceptId
|
|
142
|
+
)}
|
|
117
143
|
/>
|
|
118
144
|
))}
|
|
119
145
|
{selectedConcept && selectedConceptIsFiltered && (
|
|
@@ -141,6 +167,7 @@ ConceptSelector.propTypes = {
|
|
|
141
167
|
pageSize: PropTypes.number,
|
|
142
168
|
selectedConcept: PropTypes.object,
|
|
143
169
|
showTitle: PropTypes.bool,
|
|
170
|
+
links: PropTypes.array,
|
|
144
171
|
};
|
|
145
172
|
|
|
146
173
|
const mapStateToProps = ({ concepts }) => ({ concepts });
|
|
@@ -1,90 +1,129 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import { waitFor } from "@testing-library/react";
|
|
4
|
+
import userEvent from "@testing-library/user-event";
|
|
3
5
|
import { linkTo } from "@truedat/core/routes";
|
|
4
6
|
import { ConceptRelationForm } from "../ConceptRelationForm";
|
|
7
|
+
import en from "../../../../messages/en";
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
it("matches the latest snapshot", () => {
|
|
8
|
-
const props = {
|
|
9
|
-
tagOptions: [{ text: "-", value: 0 }],
|
|
10
|
-
concept: {}
|
|
11
|
-
};
|
|
12
|
-
const wrapper = shallowWithIntl(<ConceptRelationForm {...props} />);
|
|
13
|
-
expect(wrapper).toMatchSnapshot();
|
|
14
|
-
});
|
|
9
|
+
const linkConcept = jest.fn();
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
const domain = {
|
|
12
|
+
external_id: "foo domain",
|
|
13
|
+
id: 381,
|
|
14
|
+
name: "foo domain",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const concepts = [
|
|
18
|
+
{
|
|
19
|
+
business_concept_id: 2,
|
|
20
|
+
domain: domain,
|
|
21
|
+
id: 12355,
|
|
22
|
+
name: "foo concept",
|
|
23
|
+
status: "published",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
business_concept_id: 3,
|
|
27
|
+
domain: domain,
|
|
28
|
+
id: 12290,
|
|
29
|
+
name: "bar concept",
|
|
30
|
+
status: "draft",
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const concept = {
|
|
35
|
+
id: 1,
|
|
36
|
+
business_concept_id: 543,
|
|
37
|
+
};
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
const tagOptions = [
|
|
40
|
+
{ value: 0, text: "-" },
|
|
41
|
+
{ value: 1, text: "relates_to" },
|
|
42
|
+
];
|
|
29
43
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
const props = {
|
|
45
|
+
tagOptions,
|
|
46
|
+
concept,
|
|
47
|
+
linkConcept,
|
|
48
|
+
selectedRelationTags: [1],
|
|
49
|
+
};
|
|
34
50
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
51
|
+
const renderOpts = {
|
|
52
|
+
messages: {
|
|
53
|
+
en: {
|
|
54
|
+
...en,
|
|
55
|
+
"actions.create": "create",
|
|
56
|
+
"actions.cancel": "cancel",
|
|
57
|
+
"search.save_filters": "save filters",
|
|
58
|
+
"search.clear_filters": "clear filters",
|
|
59
|
+
"search.applied_filters": "applied filters",
|
|
60
|
+
"relations.relationType": "relation type",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
state: {
|
|
64
|
+
conceptActiveFilters: { filter: "some" },
|
|
65
|
+
concepts,
|
|
66
|
+
tagOptions,
|
|
67
|
+
},
|
|
68
|
+
fallback: "lazy",
|
|
69
|
+
};
|
|
38
70
|
|
|
39
|
-
|
|
71
|
+
describe("<ConceptRelationForm />", () => {
|
|
72
|
+
it("matches the latest snapshot", () => {
|
|
73
|
+
const { container } = render(
|
|
74
|
+
<ConceptRelationForm {...props} />,
|
|
75
|
+
renderOpts
|
|
76
|
+
);
|
|
77
|
+
expect(container).toMatchSnapshot();
|
|
40
78
|
});
|
|
41
79
|
|
|
42
|
-
it("
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
],
|
|
51
|
-
concept
|
|
52
|
-
};
|
|
53
|
-
const wrapper = shallowWithIntl(<ConceptRelationForm {...props} />);
|
|
80
|
+
it("handles tag and concept selection", async () => {
|
|
81
|
+
const { findByText, findByRole, getByRole } = render(
|
|
82
|
+
<ConceptRelationForm {...props} />,
|
|
83
|
+
renderOpts
|
|
84
|
+
);
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(getByRole("button", { name: "create" })).toBeDisabled();
|
|
87
|
+
});
|
|
54
88
|
|
|
55
|
-
|
|
89
|
+
userEvent.click(await findByText("bar concept"));
|
|
90
|
+
|
|
91
|
+
userEvent.click(await findByRole("option", { name: "relates_to" }));
|
|
92
|
+
|
|
93
|
+
await waitFor(() => {
|
|
94
|
+
expect(getByRole("button", { name: "create" })).toBeEnabled();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
56
97
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
it("submits the selected relation", async () => {
|
|
99
|
+
const { findByText, findByRole, getByRole } = render(
|
|
100
|
+
<ConceptRelationForm {...props} />,
|
|
101
|
+
renderOpts
|
|
102
|
+
);
|
|
103
|
+
userEvent.click(await findByText("bar concept"));
|
|
61
104
|
|
|
62
|
-
|
|
63
|
-
.find("Checkbox")
|
|
64
|
-
.at(1)
|
|
65
|
-
.prop("onChange")(null, { value: 2 });
|
|
105
|
+
userEvent.click(await findByRole("option", { name: "relates_to" }));
|
|
66
106
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
expect(getByRole("button", { name: "create" })).toBeEnabled();
|
|
109
|
+
});
|
|
70
110
|
|
|
71
|
-
|
|
72
|
-
.find("Button")
|
|
73
|
-
.find({ primary: true })
|
|
74
|
-
.prop("onClick")();
|
|
111
|
+
userEvent.click(await getByRole("button", { name: "create" }));
|
|
75
112
|
|
|
76
113
|
const expectedRelation = {
|
|
77
114
|
redirectUrl: linkTo.CONCEPT_LINKS_CONCEPTS(concept),
|
|
78
115
|
source_id: 543,
|
|
79
116
|
source_type: "business_concept",
|
|
80
|
-
target_id:
|
|
117
|
+
target_id: 3,
|
|
81
118
|
target_type: "business_concept",
|
|
82
|
-
tag_ids: [
|
|
119
|
+
tag_ids: [1],
|
|
83
120
|
context: {
|
|
84
|
-
target: { business_concept_id:
|
|
85
|
-
source: { business_concept_id: 543, id:
|
|
86
|
-
}
|
|
121
|
+
target: { business_concept_id: 3, id: 12290, name: "bar concept" },
|
|
122
|
+
source: { business_concept_id: 543, id: 1 },
|
|
123
|
+
},
|
|
87
124
|
};
|
|
88
|
-
|
|
125
|
+
await waitFor(() =>
|
|
126
|
+
expect(linkConcept).toHaveBeenCalledWith(expectedRelation)
|
|
127
|
+
);
|
|
89
128
|
});
|
|
90
129
|
});
|