@truedat/bg 7.2.4 → 7.2.6
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/Concept.js +112 -40
- package/src/concepts/components/ConceptDetails.js +15 -86
- package/src/concepts/components/ConceptHeader.js +78 -48
- package/src/concepts/components/ConceptTabPane.js +14 -2
- package/src/concepts/components/__tests__/Concept.spec.js +124 -5
- package/src/concepts/components/__tests__/ConceptDetails.spec.js +6 -11
- package/src/concepts/components/__tests__/ConceptHeader.spec.js +184 -37
- package/src/concepts/components/__tests__/__snapshots__/Concept.spec.js.snap +302 -47
- package/src/concepts/components/__tests__/__snapshots__/ConceptHeader.spec.js.snap +88 -54
- package/src/taxonomy/components/__tests__/__snapshots__/DomainStructures.spec.js.snap +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/bg",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.6",
|
|
4
4
|
"description": "Truedat Web Business Glossary",
|
|
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": "7.2.
|
|
37
|
+
"@truedat/test": "7.2.6",
|
|
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",
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
]
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
|
-
"@truedat/core": "7.2.
|
|
90
|
-
"@truedat/df": "7.2.
|
|
91
|
-
"@truedat/lm": "7.2.
|
|
89
|
+
"@truedat/core": "7.2.6",
|
|
90
|
+
"@truedat/df": "7.2.6",
|
|
91
|
+
"@truedat/lm": "7.2.6",
|
|
92
92
|
"decode-uri-component": "^0.2.2",
|
|
93
93
|
"file-saver": "^2.0.5",
|
|
94
94
|
"moment": "^2.29.4",
|
|
@@ -111,5 +111,5 @@
|
|
|
111
111
|
"react-dom": ">= 16.8.6 < 17",
|
|
112
112
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
113
113
|
},
|
|
114
|
-
"gitHead": "
|
|
114
|
+
"gitHead": "488b8216a78852f55db6e7e28a83c4d3fec0b890"
|
|
115
115
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
5
|
import { Route } from "react-router-dom";
|
|
6
6
|
import { Grid, Segment } from "semantic-ui-react";
|
|
7
7
|
import { Comments, CommentsLoader } from "@truedat/core/components";
|
|
8
8
|
import { CONCEPT_VERSION } from "@truedat/core/routes";
|
|
9
|
+
import { useIntl } from "react-intl";
|
|
10
|
+
import { useLanguage } from "@truedat/core/i18n";
|
|
11
|
+
import { splitTranslatableFields } from "@truedat/core/services/i18nContent";
|
|
9
12
|
import ConceptRelationsSummary from "../relations/components/ConceptRelationsSummary";
|
|
10
13
|
import ConceptArchiveLoader from "./ConceptArchiveLoader";
|
|
11
14
|
import ConceptCompleteness from "./ConceptCompleteness";
|
|
@@ -29,52 +32,121 @@ const ConceptRulesLoader = React.lazy(() =>
|
|
|
29
32
|
import("@truedat/dq/components/ConceptRulesLoader")
|
|
30
33
|
);
|
|
31
34
|
|
|
32
|
-
export const Concept = ({ id }) =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
35
|
+
export const Concept = ({ id, concept }) => {
|
|
36
|
+
const { locales } = useLanguage();
|
|
37
|
+
const { locale } = useIntl();
|
|
38
|
+
|
|
39
|
+
const defaultLang = _.flow(
|
|
40
|
+
_.find({ is_default: true }),
|
|
41
|
+
_.prop("lang")
|
|
42
|
+
)(locales);
|
|
43
|
+
|
|
44
|
+
const initialLang = concept.status == "published" ? locale : defaultLang;
|
|
45
|
+
|
|
46
|
+
const [selectedLang, setSelectedLang] = useState(initialLang);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
setSelectedLang(initialLang);
|
|
49
|
+
}, [initialLang]);
|
|
50
|
+
|
|
51
|
+
const content = _.getOr({}, "dynamic_content")(concept);
|
|
52
|
+
|
|
53
|
+
const { noTranslatable: noTranslatableFields } = splitTranslatableFields(
|
|
54
|
+
concept.template
|
|
55
|
+
);
|
|
56
|
+
const noTranslatableContent = _.pick(_.keys(noTranslatableFields))(
|
|
57
|
+
concept.dynamic_content
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const i18nContent = concept.i18n_content || {};
|
|
61
|
+
|
|
62
|
+
const i18nConcept = _.flow(
|
|
63
|
+
_.filter(({ is_enabled }) => is_enabled),
|
|
64
|
+
_.map((locale) => {
|
|
65
|
+
const { id, lang, is_default, is_required, local_name } = locale;
|
|
66
|
+
const contentLang = _.get(lang)(i18nContent);
|
|
67
|
+
const publishedWithoutLang =
|
|
68
|
+
_.isNil(contentLang) && concept.status == "published";
|
|
69
|
+
const langContent =
|
|
70
|
+
is_default || publishedWithoutLang
|
|
71
|
+
? content
|
|
72
|
+
: _.isEmpty(contentLang?.content)
|
|
73
|
+
? { ...noTranslatableContent }
|
|
74
|
+
: { ...noTranslatableContent, ...contentLang?.content };
|
|
75
|
+
|
|
76
|
+
const langName = is_default ? concept.name : contentLang?.name || "";
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
id,
|
|
80
|
+
lang,
|
|
81
|
+
isDefault: is_default,
|
|
82
|
+
isRequired: is_required,
|
|
83
|
+
name: langName,
|
|
84
|
+
langLocalName: local_name,
|
|
85
|
+
content: langContent,
|
|
86
|
+
};
|
|
87
|
+
}),
|
|
88
|
+
_.orderBy(["isDefault", "isRequired", "lang"], ["desc", "desc", "asc"]),
|
|
89
|
+
_.keyBy("lang")
|
|
90
|
+
)(locales);
|
|
91
|
+
|
|
92
|
+
const selectedContent = i18nConcept[selectedLang]?.content || concept.content;
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<>
|
|
96
|
+
<ConceptCrumbs />
|
|
97
|
+
<ConceptArchiveLoader />
|
|
98
|
+
<CommentsLoader resource_id={id} resource_type="business_concept" />
|
|
99
|
+
<EventsLoader resource_id={id} resource_type="concept" />
|
|
100
|
+
<ConceptRulesLoader />
|
|
101
|
+
<RelationsLoader
|
|
102
|
+
resource_id={id}
|
|
103
|
+
resource_type="business_concept"
|
|
104
|
+
target_type="business_concept"
|
|
105
|
+
/>
|
|
106
|
+
<Grid columns="equal" style={{ marginTop: 0 }}>
|
|
107
|
+
<Grid.Column width={12}>
|
|
108
|
+
<Segment>
|
|
109
|
+
<ConceptHeader
|
|
110
|
+
selectedLang={selectedLang}
|
|
111
|
+
setSelectedLang={setSelectedLang}
|
|
112
|
+
i18nConcept={i18nConcept}
|
|
113
|
+
/>
|
|
114
|
+
<ConceptTabs />
|
|
115
|
+
<ConceptTabPane
|
|
116
|
+
lang={selectedLang}
|
|
117
|
+
selectedContent={selectedContent}
|
|
118
|
+
/>
|
|
119
|
+
</Segment>
|
|
120
|
+
<Route
|
|
121
|
+
path={CONCEPT_VERSION}
|
|
122
|
+
exact
|
|
123
|
+
render={() => (
|
|
124
|
+
<Segment>
|
|
125
|
+
<Comments />
|
|
126
|
+
</Segment>
|
|
127
|
+
)}
|
|
128
|
+
/>
|
|
129
|
+
</Grid.Column>
|
|
130
|
+
<Grid.Column width={4}>
|
|
131
|
+
<ConceptSummary />
|
|
132
|
+
<ConceptRelationsSummary />
|
|
133
|
+
<ConceptTaxonomy />
|
|
134
|
+
<SharedToDomains />
|
|
135
|
+
<ConceptCompleteness />
|
|
136
|
+
</Grid.Column>
|
|
137
|
+
</Grid>
|
|
138
|
+
</>
|
|
139
|
+
);
|
|
140
|
+
};
|
|
71
141
|
|
|
72
142
|
Concept.propTypes = {
|
|
73
143
|
id: PropTypes.number.isRequired,
|
|
144
|
+
concept: PropTypes.object,
|
|
74
145
|
};
|
|
75
146
|
|
|
76
147
|
const mapStateToProps = ({ concept }) => ({
|
|
77
148
|
id: _.prop("business_concept_id")(concept),
|
|
149
|
+
concept: concept,
|
|
78
150
|
});
|
|
79
151
|
|
|
80
152
|
export default connect(mapStateToProps)(Concept);
|
|
@@ -1,105 +1,34 @@
|
|
|
1
|
-
import
|
|
2
|
-
import React, { useEffect, useState } from "react";
|
|
1
|
+
import React from "react";
|
|
3
2
|
import PropTypes from "prop-types";
|
|
4
3
|
import { connect } from "react-redux";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { useLanguage } from "@truedat/core/i18n";
|
|
8
|
-
import { splitTranslatableFields } from "@truedat/core/services/i18nContent";
|
|
4
|
+
import { Segment } from "semantic-ui-react";
|
|
5
|
+
import { I18nProvider } from "@truedat/core/i18n";
|
|
9
6
|
|
|
10
7
|
const DynamicFormViewer = React.lazy(() =>
|
|
11
8
|
import("@truedat/df/components/DynamicFormViewer")
|
|
12
9
|
);
|
|
13
10
|
|
|
14
|
-
export const ConceptDetails = ({ concept }) => {
|
|
15
|
-
const { locale } = useIntl();
|
|
16
|
-
const { locales } = useLanguage();
|
|
17
|
-
|
|
18
|
-
const defaultLang = _.flow(
|
|
19
|
-
_.find({ is_default: true }),
|
|
20
|
-
_.prop("lang")
|
|
21
|
-
)(locales);
|
|
22
|
-
|
|
23
|
-
const initialLang = concept.status == "published" ? locale : defaultLang;
|
|
24
|
-
|
|
25
|
-
const [selectedLang, setSelectedLang] = useState(initialLang);
|
|
26
|
-
useEffect(() => {
|
|
27
|
-
setSelectedLang(initialLang);
|
|
28
|
-
}, [initialLang]);
|
|
29
|
-
|
|
30
|
-
const content = _.getOr({}, "dynamic_content")(concept);
|
|
31
|
-
|
|
32
|
-
const { noTranslatable: noTranslatableFields } = splitTranslatableFields(
|
|
33
|
-
concept.template
|
|
34
|
-
);
|
|
35
|
-
const noTranslatableContent = _.pick(_.keys(noTranslatableFields))(
|
|
36
|
-
concept.dynamic_content
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const i18nContent = concept.i18n_content || {};
|
|
40
|
-
|
|
41
|
-
const i18nConcept = _.flow(
|
|
42
|
-
_.filter(({ is_enabled }) => is_enabled),
|
|
43
|
-
_.map((locale) => {
|
|
44
|
-
const { id, lang, is_default, is_required, local_name } = locale;
|
|
45
|
-
const contentLang = _.get(lang)(i18nContent);
|
|
46
|
-
const publishedWithoutLang =
|
|
47
|
-
_.isNil(contentLang) && concept.status == "published";
|
|
48
|
-
const langContent =
|
|
49
|
-
is_default || publishedWithoutLang
|
|
50
|
-
? content
|
|
51
|
-
: _.isEmpty(contentLang?.content)
|
|
52
|
-
? { ...noTranslatableContent }
|
|
53
|
-
: { ...noTranslatableContent, ...contentLang?.content };
|
|
54
|
-
|
|
55
|
-
const langName = is_default ? concept.name : contentLang?.name || "";
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
id,
|
|
59
|
-
lang,
|
|
60
|
-
isDefault: is_default,
|
|
61
|
-
isRequired: is_required,
|
|
62
|
-
name: langName,
|
|
63
|
-
langLocalName: local_name,
|
|
64
|
-
content: langContent,
|
|
65
|
-
};
|
|
66
|
-
}),
|
|
67
|
-
_.orderBy(["isDefault", "isRequired", "lang"], ["desc", "desc", "asc"]),
|
|
68
|
-
_.keyBy("lang")
|
|
69
|
-
)(locales);
|
|
70
|
-
|
|
11
|
+
export const ConceptDetails = ({ concept, selectedContent, lang }) => {
|
|
71
12
|
return (
|
|
72
13
|
<Segment attached="bottom">
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
<Button
|
|
80
|
-
key={lang}
|
|
81
|
-
content={lang}
|
|
82
|
-
size="mini"
|
|
83
|
-
onClick={() => setSelectedLang(lang)}
|
|
84
|
-
active={selectedLang == lang}
|
|
85
|
-
/>
|
|
86
|
-
) : null;
|
|
87
|
-
})
|
|
88
|
-
)(i18nConcept)}
|
|
89
|
-
</ButtonGroup>
|
|
90
|
-
) : null}
|
|
91
|
-
<DynamicFormViewer
|
|
92
|
-
template={concept.template}
|
|
93
|
-
content={i18nConcept[selectedLang]?.content || concept.content}
|
|
94
|
-
/>
|
|
14
|
+
<I18nProvider lang={lang}>
|
|
15
|
+
<DynamicFormViewer
|
|
16
|
+
template={concept.template}
|
|
17
|
+
content={selectedContent}
|
|
18
|
+
/>
|
|
19
|
+
</I18nProvider>
|
|
95
20
|
</Segment>
|
|
96
21
|
);
|
|
97
22
|
};
|
|
98
23
|
|
|
99
24
|
ConceptDetails.propTypes = {
|
|
100
25
|
concept: PropTypes.object,
|
|
26
|
+
selectedContent: PropTypes.object,
|
|
27
|
+
lang: PropTypes.string,
|
|
101
28
|
};
|
|
102
29
|
|
|
103
|
-
const mapStateToProps = ({ concept }) => ({
|
|
30
|
+
const mapStateToProps = ({ concept }) => ({
|
|
31
|
+
concept,
|
|
32
|
+
});
|
|
104
33
|
|
|
105
34
|
export default connect(mapStateToProps)(ConceptDetails);
|
|
@@ -3,7 +3,14 @@ import React from "react";
|
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
5
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Grid,
|
|
8
|
+
Header,
|
|
9
|
+
Icon,
|
|
10
|
+
Button,
|
|
11
|
+
ButtonGroup,
|
|
12
|
+
GridRow,
|
|
13
|
+
} from "semantic-ui-react";
|
|
7
14
|
import { linkTo } from "@truedat/core/routes";
|
|
8
15
|
import ShareLinkPopup from "@truedat/audit/components/ShareLinkPopup";
|
|
9
16
|
import ConceptConfidentialButton from "./ConceptConfidentialButton";
|
|
@@ -15,16 +22,22 @@ export const ConceptHeader = ({
|
|
|
15
22
|
concept,
|
|
16
23
|
setConfidentialConcept,
|
|
17
24
|
share,
|
|
18
|
-
|
|
25
|
+
selectedLang,
|
|
26
|
+
setSelectedLang,
|
|
27
|
+
i18nConcept,
|
|
19
28
|
}) => {
|
|
20
|
-
const {
|
|
29
|
+
const { formatMessage } = useIntl();
|
|
30
|
+
const template = concept.template;
|
|
21
31
|
|
|
22
32
|
const path = linkTo.CONCEPT_VERSION({
|
|
23
33
|
business_concept_id: _.prop("business_concept_id")(concept),
|
|
24
34
|
id: "current",
|
|
25
35
|
});
|
|
26
36
|
|
|
27
|
-
const i18nContent = _.flow(
|
|
37
|
+
const i18nContent = _.flow(
|
|
38
|
+
_.get("i18n_content"),
|
|
39
|
+
_.get(selectedLang)
|
|
40
|
+
)(concept);
|
|
28
41
|
|
|
29
42
|
const name = _.isUndefined(i18nContent) ? concept?.name : i18nContent?.name;
|
|
30
43
|
const shareUrl = `${location.protocol}//${location.host}${path}`;
|
|
@@ -33,63 +46,80 @@ export const ConceptHeader = ({
|
|
|
33
46
|
|
|
34
47
|
return (
|
|
35
48
|
<Grid>
|
|
36
|
-
<
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<Grid>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
49
|
+
<GridRow>
|
|
50
|
+
<Grid.Column width={8}>
|
|
51
|
+
<Header as="h2">
|
|
52
|
+
<Icon circular name="book" />
|
|
53
|
+
<Header.Content>
|
|
54
|
+
{name}
|
|
55
|
+
<Header.Subheader>
|
|
56
|
+
<FormattedMessage
|
|
57
|
+
id={`templates.${templateLabel}`}
|
|
58
|
+
defaultMessage={templateLabel}
|
|
59
|
+
/>
|
|
60
|
+
</Header.Subheader>
|
|
61
|
+
</Header.Content>
|
|
62
|
+
</Header>
|
|
63
|
+
</Grid.Column>
|
|
64
|
+
<Grid.Column width={8} textAlign="right">
|
|
65
|
+
<ShareLinkPopup
|
|
66
|
+
url={shareUrl}
|
|
67
|
+
title={shareTitle}
|
|
68
|
+
name={name}
|
|
69
|
+
icon="concepts"
|
|
70
|
+
popupType="concepts"
|
|
71
|
+
/>
|
|
72
|
+
{share && <SharedToDomainsPopup />}
|
|
73
|
+
<ConceptSubscription />
|
|
74
|
+
{!_.isEmpty(setConfidentialConcept) ? (
|
|
75
|
+
<ConceptConfidentialButton concept={concept} />
|
|
76
|
+
) : null}
|
|
77
|
+
</Grid.Column>
|
|
78
|
+
</GridRow>
|
|
79
|
+
<GridRow>
|
|
80
|
+
<Grid.Column width={8}>
|
|
81
|
+
{!_.includes(concept.status)(["published", "versioned"]) &&
|
|
82
|
+
_.size(i18nConcept) > 1 ? (
|
|
83
|
+
<ButtonGroup floated="left">
|
|
84
|
+
{_.flow(
|
|
85
|
+
_.map(({ lang, content }) => {
|
|
86
|
+
return !_.isEmpty(content) ? (
|
|
87
|
+
<Button
|
|
88
|
+
key={lang}
|
|
89
|
+
content={
|
|
90
|
+
<FormattedMessage id={"i18n.messages.lang." + lang} />
|
|
91
|
+
}
|
|
92
|
+
size="mini"
|
|
93
|
+
onClick={() => setSelectedLang(lang)}
|
|
94
|
+
active={selectedLang == lang}
|
|
95
|
+
/>
|
|
96
|
+
) : null;
|
|
97
|
+
})
|
|
98
|
+
)(i18nConcept)}
|
|
99
|
+
</ButtonGroup>
|
|
100
|
+
) : null}
|
|
101
|
+
</Grid.Column>
|
|
102
|
+
<Grid.Column width={8} textAlign="right">
|
|
103
|
+
<ConceptActions />
|
|
104
|
+
</Grid.Column>
|
|
105
|
+
</GridRow>
|
|
75
106
|
</Grid>
|
|
76
107
|
);
|
|
77
108
|
};
|
|
78
109
|
|
|
79
110
|
ConceptHeader.propTypes = {
|
|
80
111
|
setConfidentialConcept: PropTypes.object,
|
|
81
|
-
share: PropTypes.
|
|
112
|
+
share: PropTypes.object,
|
|
82
113
|
concept: PropTypes.object,
|
|
83
|
-
|
|
114
|
+
selectedLang: PropTypes.string,
|
|
115
|
+
setSelectedLang: PropTypes.func,
|
|
116
|
+
i18nConcept: PropTypes.object,
|
|
84
117
|
};
|
|
85
118
|
|
|
86
|
-
const EMPTY = {};
|
|
87
|
-
|
|
88
119
|
const mapStateToProps = ({ concept, conceptActions, conceptPermissions }) => ({
|
|
89
120
|
share: _.prop("share")(conceptPermissions),
|
|
90
121
|
concept,
|
|
91
122
|
setConfidentialConcept: _.prop("set_confidential")(conceptActions),
|
|
92
|
-
template: concept.template || EMPTY,
|
|
93
123
|
});
|
|
94
124
|
|
|
95
125
|
export default connect(mapStateToProps)(ConceptHeader);
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
CONCEPT_RULES_NEW,
|
|
12
12
|
CONCEPT_VERSION,
|
|
13
13
|
} from "@truedat/core/routes";
|
|
14
|
+
import PropTypes from "prop-types";
|
|
14
15
|
import ConceptRelationsRoutes from "../relations/components/ConceptRelationsRoutes";
|
|
15
16
|
import ConceptArchive from "./ConceptArchive";
|
|
16
17
|
import ConceptDetails from "./ConceptDetails";
|
|
@@ -22,10 +23,16 @@ const ConceptRules = React.lazy(() =>
|
|
|
22
23
|
|
|
23
24
|
const NewRule = React.lazy(() => import("@truedat/dq/components/NewRule"));
|
|
24
25
|
|
|
25
|
-
export const ConceptTabPane = () => (
|
|
26
|
+
export const ConceptTabPane = ({ selectedContent, lang }) => (
|
|
26
27
|
<ErrorBoundary>
|
|
27
28
|
<Switch>
|
|
28
|
-
<Route
|
|
29
|
+
<Route
|
|
30
|
+
path={CONCEPT_VERSION}
|
|
31
|
+
exact
|
|
32
|
+
render={() => (
|
|
33
|
+
<ConceptDetails selectedContent={selectedContent} lang={lang} />
|
|
34
|
+
)}
|
|
35
|
+
/>
|
|
29
36
|
<Route
|
|
30
37
|
path={CONCEPT_LINKS_STRUCTURES}
|
|
31
38
|
render={() => <ConceptRelationsRoutes />}
|
|
@@ -53,4 +60,9 @@ export const ConceptTabPane = () => (
|
|
|
53
60
|
</ErrorBoundary>
|
|
54
61
|
);
|
|
55
62
|
|
|
63
|
+
ConceptTabPane.propTypes = {
|
|
64
|
+
lang: PropTypes.string,
|
|
65
|
+
selectedContent: PropTypes.object,
|
|
66
|
+
};
|
|
67
|
+
|
|
56
68
|
export default ConceptTabPane;
|
|
@@ -1,12 +1,131 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { render } from "@truedat/test/render";
|
|
3
|
+
import { waitFor } from "@testing-library/react";
|
|
4
|
+
import { LangProviderWrapper } from "@truedat/core/i18n";
|
|
3
5
|
import { Concept } from "../Concept";
|
|
4
6
|
|
|
7
|
+
beforeAll(() => {
|
|
8
|
+
jest.useFakeTimers();
|
|
9
|
+
jest.setSystemTime(new Date("2025-01-01T10:00:00Z"));
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
afterAll(() => {
|
|
13
|
+
jest.useRealTimers();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const renderOpts = {
|
|
17
|
+
messages: {
|
|
18
|
+
en: {
|
|
19
|
+
"concepts.crumbs.top": "Business Glossary",
|
|
20
|
+
"concepts.crumbs.pending": "Draft",
|
|
21
|
+
"i18n.messages.lang.es": "Spanish",
|
|
22
|
+
"i18n.messages.lang.en": "English",
|
|
23
|
+
"tabs.bg.concept": "Concept",
|
|
24
|
+
"tabs.bg.relations_data_field": "Linkage",
|
|
25
|
+
"tabs.bg.qualityRules": "Quality Rules",
|
|
26
|
+
"tabs.bg.history": "History",
|
|
27
|
+
"tabs.bg.audit": "Audit",
|
|
28
|
+
"concept.props.status": "Status",
|
|
29
|
+
"concept.props.version": "Version",
|
|
30
|
+
"concept.props.last_update_at": "Update",
|
|
31
|
+
"concept.props.last_update_by": "User",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
state: {
|
|
35
|
+
concept: {
|
|
36
|
+
id: 1,
|
|
37
|
+
business_concept_id: 1,
|
|
38
|
+
name: "Concept_en",
|
|
39
|
+
type: "type",
|
|
40
|
+
status: "draft",
|
|
41
|
+
last_change_at: "2025-01-01T10:00:00Z",
|
|
42
|
+
template: {
|
|
43
|
+
content: [
|
|
44
|
+
{
|
|
45
|
+
name: "foo_group",
|
|
46
|
+
fields: [
|
|
47
|
+
{ name: "field", label: "field_label", values: ["value1"] },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
i18n_content: {
|
|
53
|
+
es: {
|
|
54
|
+
field: "field_label",
|
|
55
|
+
content: { values: ["value"] },
|
|
56
|
+
completeness: 1.0,
|
|
57
|
+
name: "Concept_es",
|
|
58
|
+
},
|
|
59
|
+
en: {
|
|
60
|
+
field: "field_label",
|
|
61
|
+
content: { values: ["value1"] },
|
|
62
|
+
completeness: 1.0,
|
|
63
|
+
name: "Concept_en",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
dynamic_content: {
|
|
67
|
+
field: { value: "value1", origin: "user" },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
locales: [
|
|
71
|
+
{ id: 1, lang: "en", is_default: true, is_required: true },
|
|
72
|
+
{ id: 2, lang: "es", is_default: false, is_required: false },
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
fallback: "lazy",
|
|
76
|
+
};
|
|
77
|
+
|
|
5
78
|
describe("<Concept />", () => {
|
|
6
|
-
const props = {
|
|
79
|
+
const props = {
|
|
80
|
+
id: 1,
|
|
81
|
+
concept: {
|
|
82
|
+
id: 1,
|
|
83
|
+
business_concept_id: 1,
|
|
84
|
+
name: "Concept1",
|
|
85
|
+
type: "type",
|
|
86
|
+
status: "draft",
|
|
87
|
+
last_change_at: "2024-01-01T10:00:00Z",
|
|
88
|
+
template: {
|
|
89
|
+
content: [
|
|
90
|
+
{
|
|
91
|
+
name: "foo_group",
|
|
92
|
+
fields: [
|
|
93
|
+
{ name: "field", label: "field_label", values: ["value1"] },
|
|
94
|
+
],
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
i18n_content: {
|
|
99
|
+
es: {
|
|
100
|
+
field: "field_label",
|
|
101
|
+
content: { values: ["value1"] },
|
|
102
|
+
completeness: 1.0,
|
|
103
|
+
name: "Concepto1",
|
|
104
|
+
},
|
|
105
|
+
en: {
|
|
106
|
+
field: "field_label",
|
|
107
|
+
content: { values: ["value1"] },
|
|
108
|
+
completeness: 1.0,
|
|
109
|
+
name: "Concept1",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
dynamic_content: {
|
|
113
|
+
field: { value: "value1", origin: "user" },
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
};
|
|
7
117
|
|
|
8
|
-
it("matches the latest snapshot", () => {
|
|
9
|
-
const
|
|
10
|
-
|
|
118
|
+
it("matches the latest snapshot", async () => {
|
|
119
|
+
const { container, queryByText } = render(
|
|
120
|
+
<LangProviderWrapper langs={["es", "en"]}>
|
|
121
|
+
<Concept {...props} />
|
|
122
|
+
</LangProviderWrapper>,
|
|
123
|
+
renderOpts
|
|
124
|
+
);
|
|
125
|
+
await waitFor(() => {
|
|
126
|
+
expect(queryByText(/lazy/i)).not.toBeInTheDocument();
|
|
127
|
+
expect(container.querySelector(".loading")).not.toBeInTheDocument();
|
|
128
|
+
});
|
|
129
|
+
expect(container).toMatchSnapshot();
|
|
11
130
|
});
|
|
12
131
|
});
|