@truedat/dq 4.42.4 → 4.43.0

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +5 -5
  3. package/src/api.js +6 -0
  4. package/src/components/ImplementationStructureDelete.js +41 -0
  5. package/src/components/ImplementationStructureLink.js +24 -0
  6. package/src/components/ImplementationStructures.js +138 -0
  7. package/src/components/ImplementationStructuresNew.js +125 -0
  8. package/src/components/RuleImplementationTabs.js +16 -1
  9. package/src/components/RuleImplementationsTable.js +10 -3
  10. package/src/components/RuleRoutes.js +37 -1
  11. package/src/components/__tests__/ImplementationStructureDelete.spec.js +27 -0
  12. package/src/components/__tests__/ImplementationStructureLink.spec.js +23 -0
  13. package/src/components/__tests__/ImplementationStructures.spec.js +88 -0
  14. package/src/components/__tests__/ImplementationStructuresNew.spec.js +34 -0
  15. package/src/components/__tests__/RuleImplementation.spec.js +2 -1
  16. package/src/components/__tests__/RuleImplementationTabs.spec.js +2 -1
  17. package/src/components/__tests__/__snapshots__/ImplementationStructureDelete.spec.js.snap +10 -0
  18. package/src/components/__tests__/__snapshots__/ImplementationStructureLink.spec.js.snap +11 -0
  19. package/src/components/__tests__/__snapshots__/ImplementationStructures.spec.js.snap +208 -0
  20. package/src/components/__tests__/__snapshots__/ImplementationStructuresNew.spec.js.snap +106 -0
  21. package/src/components/__tests__/__snapshots__/RuleImplementation.spec.js.snap +7 -1
  22. package/src/components/__tests__/__snapshots__/RuleImplementationTabs.spec.js.snap +8 -2
  23. package/src/messages/en.js +13 -1
  24. package/src/messages/es.js +14 -1
  25. package/src/reducers/ruleImplementation.js +1 -0
  26. package/src/reducers/ruleImplementationRedirect.js +4 -0
  27. package/src/routines.js +7 -0
  28. package/src/sagas/__tests__/createImplementationStructure.spec.js +86 -0
  29. package/src/sagas/__tests__/deleteImplementationStructure.spec.js +84 -0
  30. package/src/sagas/createImplementationStructure.js +32 -0
  31. package/src/sagas/deleteImplementationStructure.js +37 -0
  32. package/src/sagas/index.js +16 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.43.0] 2022-04-26
4
+
5
+ # Added
6
+
7
+ - [TD-3186] Support for linking Implementations and Structures
8
+
3
9
  ## [4.42.3] 2022-04-13
4
10
 
5
11
  # Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dq",
3
- "version": "4.42.4",
3
+ "version": "4.43.0",
4
4
  "description": "Truedat Web Data Quality Module",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -31,7 +31,7 @@
31
31
  "@babel/plugin-transform-modules-commonjs": "^7.15.0",
32
32
  "@babel/preset-env": "^7.15.0",
33
33
  "@babel/preset-react": "^7.14.5",
34
- "@truedat/test": "4.42.4",
34
+ "@truedat/test": "4.43.0",
35
35
  "babel-jest": "^27.0.6",
36
36
  "babel-plugin-dynamic-import-node": "^2.3.3",
37
37
  "babel-plugin-lodash": "^3.3.4",
@@ -82,8 +82,8 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@apollo/client": "^3.4.10",
85
- "@truedat/core": "4.42.4",
86
- "@truedat/df": "4.42.4",
85
+ "@truedat/core": "4.43.0",
86
+ "@truedat/df": "4.43.0",
87
87
  "axios": "^0.19.2",
88
88
  "graphql": "^15.5.3",
89
89
  "path-to-regexp": "^1.7.0",
@@ -103,5 +103,5 @@
103
103
  "react-dom": ">= 16.8.6 < 17",
104
104
  "semantic-ui-react": ">= 0.88.2 < 2.1"
105
105
  },
106
- "gitHead": "3130f82b417611c76542fc29a6885b4bf85cdd30"
106
+ "gitHead": "ad0f63f2d98ae377883743b839858d9d671d2431"
107
107
  }
package/src/api.js CHANGED
@@ -1,6 +1,10 @@
1
1
  const API_CONCEPT_RULES = "/api/rules/concept/:id";
2
2
  const API_EXECUTION_GROUP = "/api/execution_groups/:id";
3
3
  const API_EXECUTION_GROUPS = "/api/execution_groups";
4
+ const API_IMPLEMENTATIONS_STRUCTURES =
5
+ "/api/rule_implementations/:implementationId/data_structures";
6
+ const API_IMPLEMENTATIONS_STRUCTURE =
7
+ "/api/rule_implementations/data_structures/:id";
4
8
  const API_RULE = "/api/rules/:id";
5
9
  const API_REMEDIATION_PLAN = "/api/rule_results/:rule_result_id/remediation";
6
10
  const API_RULES = "/api/rules";
@@ -26,6 +30,8 @@ export {
26
30
  API_CONCEPT_RULES,
27
31
  API_EXECUTION_GROUP,
28
32
  API_EXECUTION_GROUPS,
33
+ API_IMPLEMENTATIONS_STRUCTURES,
34
+ API_IMPLEMENTATIONS_STRUCTURE,
29
35
  API_REMEDIATION_PLAN,
30
36
  API_RULE,
31
37
  API_RULES,
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import { connect } from "react-redux";
4
+ import { Icon } from "semantic-ui-react";
5
+ import { FormattedMessage } from "react-intl";
6
+ import { ConfirmModal } from "@truedat/core/components";
7
+ import { deleteImplementationStructure } from "../routines";
8
+
9
+ export const ImplementationStructureDelete = ({
10
+ implementationId,
11
+ id,
12
+ deleteImplementationStructure,
13
+ }) => (
14
+ <ConfirmModal
15
+ icon="trash"
16
+ trigger={<Icon name="trash alternate outline" color="red" />}
17
+ header={
18
+ <FormattedMessage
19
+ id={"implementationsStructures.delete.confirmation.header"}
20
+ />
21
+ }
22
+ content={
23
+ <FormattedMessage
24
+ id={"implementationsStructures.delete.confirmation.content"}
25
+ />
26
+ }
27
+ onConfirm={() => deleteImplementationStructure({ implementationId, id })}
28
+ onOpen={(e) => e.stopPropagation()}
29
+ onClose={(e) => e.stopPropagation()}
30
+ />
31
+ );
32
+
33
+ ImplementationStructureDelete.propTypes = {
34
+ implementationId: PropTypes.number,
35
+ id: PropTypes.number,
36
+ deleteImplementationStructure: PropTypes.func,
37
+ };
38
+
39
+ export default connect(null, { deleteImplementationStructure })(
40
+ ImplementationStructureDelete
41
+ );
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import { Link } from "react-router-dom";
3
+ import PropTypes from "prop-types";
4
+ import { linkTo } from "@truedat/core/routes";
5
+
6
+ export const ImplementationStructureLink = ({
7
+ id,
8
+ current_version: { name },
9
+ }) => (
10
+ <Link
11
+ to={linkTo.STRUCTURE({
12
+ id,
13
+ })}
14
+ >
15
+ {name}
16
+ </Link>
17
+ );
18
+
19
+ ImplementationStructureLink.propTypes = {
20
+ id: PropTypes.number,
21
+ current_version: PropTypes.object,
22
+ };
23
+
24
+ export default ImplementationStructureLink;
@@ -0,0 +1,138 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { Table, Button, Grid, Segment, Header, Icon } from "semantic-ui-react";
6
+ import { FormattedMessage } from "react-intl";
7
+ import { Link } from "react-router-dom";
8
+ import { linkTo } from "@truedat/core/routes";
9
+ import { columnDecorator } from "@truedat/core/services";
10
+ import ImplementationStructureDelete from "./ImplementationStructureDelete";
11
+ import ImplementationStructureLink from "./ImplementationStructureLink";
12
+
13
+ const ImplementationStructureDeleteDecorator = ({ id, implementation_id }) => (
14
+ <ImplementationStructureDelete id={id} implementationId={implementation_id} />
15
+ );
16
+ ImplementationStructureDeleteDecorator.propTypes = {
17
+ id: PropTypes.number,
18
+ implementation_id: PropTypes.number,
19
+ };
20
+
21
+ const PathDecorator = (path) => (
22
+ <span title={_.join(" › ")(path)}>
23
+ {_.flow(_.join(" › "), _.truncate({ length: 90 }))(path)}
24
+ </span>
25
+ );
26
+ PathDecorator.propTypes = {
27
+ path: PropTypes.string,
28
+ };
29
+
30
+ const ImplementationStructureTypeDecorator = (type) => (
31
+ <FormattedMessage id={`implementationsStructures.type.${type}`} />
32
+ );
33
+ ImplementationStructureTypeDecorator.propTypes = {
34
+ type: PropTypes.string,
35
+ };
36
+
37
+ export const ImplementationStructures = ({ implementation, canCreateLink }) => {
38
+ const deleteColumn = canCreateLink
39
+ ? [
40
+ {
41
+ fieldSelector: _.identity,
42
+ fieldDecorator: ImplementationStructureDeleteDecorator,
43
+ },
44
+ ]
45
+ : [];
46
+
47
+ const columns = [
48
+ {
49
+ header: "structure.name",
50
+ fieldSelector: _.path("data_structure"),
51
+ fieldDecorator: ImplementationStructureLink,
52
+ },
53
+ {
54
+ header: "structure.system",
55
+ fieldSelector: _.path("data_structure.system.name"),
56
+ },
57
+ {
58
+ header: "structure.path",
59
+ fieldSelector: _.path("data_structure.current_version.path"),
60
+ fieldDecorator: PathDecorator,
61
+ },
62
+ {
63
+ header: "implementationsStructures.type",
64
+ fieldSelector: _.path("type"),
65
+ fieldDecorator: ImplementationStructureTypeDecorator,
66
+ },
67
+ ...deleteColumn,
68
+ ];
69
+
70
+ const headerRow = columns.map(({ header }, key) => (
71
+ <Table.HeaderCell
72
+ key={key}
73
+ content={header ? <FormattedMessage id={header} /> : null}
74
+ />
75
+ ));
76
+
77
+ const id = _.prop("rule_id")(implementation);
78
+ const implementation_id = _.prop("id")(implementation);
79
+
80
+ return (
81
+ <Segment attached="bottom">
82
+ <Grid>
83
+ {id && canCreateLink && (
84
+ <Grid.Column width={16}>
85
+ <Button
86
+ floated="right"
87
+ primary
88
+ as={Link}
89
+ to={linkTo.IMPLEMENTATION_STRUCTURES_NEW({
90
+ id,
91
+ implementation_id,
92
+ })}
93
+ content={<FormattedMessage id="links.actions.create" />}
94
+ />
95
+ </Grid.Column>
96
+ )}
97
+ {!_.isEmpty(implementation?.data_structures) ? (
98
+ <Grid.Column width={16}>
99
+ <Table
100
+ headerRow={headerRow}
101
+ renderBodyRow={(link, i) => (
102
+ <Table.Row key={i}>
103
+ {columns.map((column, ci) => (
104
+ <Table.Cell
105
+ key={ci}
106
+ textAlign={_.prop("textAlign")(column)}
107
+ content={columnDecorator(column)(link)}
108
+ />
109
+ ))}
110
+ </Table.Row>
111
+ )}
112
+ tableData={implementation?.data_structures}
113
+ />
114
+ </Grid.Column>
115
+ ) : (
116
+ <Header as="h4">
117
+ <Icon name="search" />
118
+ <Header.Content>
119
+ <FormattedMessage id="implementationStructures.empty" />
120
+ </Header.Content>
121
+ </Header>
122
+ )}
123
+ </Grid>
124
+ </Segment>
125
+ );
126
+ };
127
+
128
+ ImplementationStructures.propTypes = {
129
+ implementation: PropTypes.object,
130
+ canCreateLink: PropTypes.bool,
131
+ };
132
+
133
+ const mapStateToProps = (state) => ({
134
+ implementation: state.ruleImplementation,
135
+ canCreateLink: _.propOr(false, "link_structure")(state.implementationActions),
136
+ });
137
+
138
+ export default connect(mapStateToProps)(ImplementationStructures);
@@ -0,0 +1,125 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
5
+ import { useIntl } from "react-intl";
6
+ import { Button, Dropdown, Grid, Header, Segment } from "semantic-ui-react";
7
+ import { HistoryBackButton } from "@truedat/core/components";
8
+ import { linkTo } from "@truedat/core/routes";
9
+ import { createImplementationStructure } from "../routines";
10
+
11
+ const StructureSelector = React.lazy(() =>
12
+ import("@truedat/dd/components/StructureSelector")
13
+ );
14
+
15
+ export const ImplementationStructuresNew = ({
16
+ creatingImplementationStructure,
17
+ implementation,
18
+ createImplementationStructure,
19
+ }) => {
20
+ const { formatMessage } = useIntl();
21
+ const [selectedTarget, setSelectedTarget] = useState(null);
22
+ const [type, setType] = useState("dataset");
23
+ const submitDisabled = creatingImplementationStructure || !selectedTarget;
24
+
25
+ const handleTargetSelected = (selectedTarget) =>
26
+ setSelectedTarget(selectedTarget);
27
+
28
+ const handleSubmit = () => {
29
+ const redirectUrl = linkTo.IMPLEMENTATION_STRUCTURES({
30
+ id: implementation.rule_id,
31
+ implementation_id: implementation.id,
32
+ });
33
+
34
+ createImplementationStructure({
35
+ implementationId: implementation.id,
36
+ dataStructureId: selectedTarget?.id,
37
+ type,
38
+ redirectUrl,
39
+ });
40
+ };
41
+
42
+ const handleTypeChange = (e, { value }) => {
43
+ if (type !== value && (value === "dataset" || type === "dataset")) {
44
+ // Reset selectTarget only if changing type from or to "dataset"
45
+ // because structure class changes
46
+ setSelectedTarget(null);
47
+ }
48
+ setType(value);
49
+ };
50
+
51
+ const typeOptions = ["dataset", "population", "validation"].map((type) => ({
52
+ key: type,
53
+ value: type,
54
+ text: formatMessage({ id: `implementationsStructures.type.${type}` }),
55
+ }));
56
+
57
+ return (
58
+ <Segment attached="bottom">
59
+ <Grid>
60
+ <Grid.Column with={16}>
61
+ <Header
62
+ as="h4"
63
+ content={formatMessage({
64
+ id: "implementationsStructures.new.header",
65
+ })}
66
+ />
67
+ <Dropdown
68
+ placeholder={formatMessage({
69
+ id: "implementationsStructures.type",
70
+ })}
71
+ selection
72
+ value={type}
73
+ onChange={handleTypeChange}
74
+ options={typeOptions}
75
+ />
76
+ {
77
+ // Duplicated component and 'keys' to force Selector rerender on changing type to clean selection
78
+ type === "dataset" ? (
79
+ <StructureSelector
80
+ key="datasetSelector"
81
+ onSelect={handleTargetSelected}
82
+ defaultFilters={{ "class.raw": ["table"] }}
83
+ />
84
+ ) : (
85
+ <StructureSelector
86
+ key="nonDatasetSelector"
87
+ onSelect={handleTargetSelected}
88
+ defaultFilters={{ "class.raw": ["field"] }}
89
+ />
90
+ )
91
+ }
92
+ <div className="actions">
93
+ <HistoryBackButton
94
+ content={formatMessage({ id: "actions.cancel" })}
95
+ disabled={creatingImplementationStructure}
96
+ />
97
+
98
+ <Button
99
+ primary
100
+ content={formatMessage({ id: "actions.create" })}
101
+ disabled={submitDisabled}
102
+ loading={creatingImplementationStructure}
103
+ onClick={handleSubmit}
104
+ />
105
+ </div>
106
+ </Grid.Column>
107
+ </Grid>
108
+ </Segment>
109
+ );
110
+ };
111
+
112
+ ImplementationStructuresNew.propTypes = {
113
+ implementation: PropTypes.object,
114
+ creatingImplementationStructure: PropTypes.bool,
115
+ createImplementationStructure: PropTypes.func,
116
+ };
117
+
118
+ const mapStateToProps = (state) => ({
119
+ creatingImplementationStructure: state.creatingImplementationStructure,
120
+ implementation: state.ruleImplementation,
121
+ });
122
+
123
+ export default connect(mapStateToProps, { createImplementationStructure })(
124
+ ImplementationStructuresNew
125
+ );
@@ -10,6 +10,8 @@ import {
10
10
  RULE_IMPLEMENTATION_EVENTS,
11
11
  IMPLEMENTATION_CONCEPT_LINKS,
12
12
  IMPLEMENTATION_CONCEPT_LINKS_NEW,
13
+ IMPLEMENTATION_STRUCTURES,
14
+ IMPLEMENTATION_STRUCTURES_NEW,
13
15
  RULE_IMPLEMENTATION_MOVE,
14
16
  RULE_IMPLEMENTATION_RESULTS_DETAILS,
15
17
  RULE_IMPLEMENTATION_RESULTS,
@@ -57,9 +59,22 @@ export const RuleImplementationTabs = ({
57
59
  implementation_id: ruleImplementation.id,
58
60
  })}
59
61
  >
60
- <FormattedMessage id="tabs.dq.implementation.links" />
62
+ <FormattedMessage id="tabs.dq.implementation.links.concepts" />
61
63
  </Menu.Item>
62
64
  )}
65
+ <Menu.Item
66
+ active={
67
+ match.path === IMPLEMENTATION_STRUCTURES ||
68
+ match.path === IMPLEMENTATION_STRUCTURES_NEW
69
+ }
70
+ as={Link}
71
+ to={linkTo.IMPLEMENTATION_STRUCTURES({
72
+ id: rule.id,
73
+ implementation_id: ruleImplementation.id,
74
+ })}
75
+ >
76
+ <FormattedMessage id="tabs.dq.implementation.structures" />
77
+ </Menu.Item>
63
78
  <Menu.Item
64
79
  active={match.path === RULE_IMPLEMENTATION_RESULTS}
65
80
  as={Link}
@@ -15,6 +15,7 @@ export const RuleImplementationsTable = ({
15
15
  additionalColumns = [],
16
16
  checkedAll,
17
17
  checkRow,
18
+ structure,
18
19
  executeImplementationsOn,
19
20
  implementationsSort,
20
21
  sortImplementations,
@@ -127,6 +128,7 @@ RuleImplementationsTable.propTypes = {
127
128
  isRowChecked: PropTypes.bool,
128
129
  ruleImplementationsLoading: PropTypes.bool,
129
130
  sortImplementations: PropTypes.func,
131
+ structure: PropTypes.object,
130
132
  ruleImplementations: PropTypes.array,
131
133
  columns: PropTypes.array,
132
134
  withoutColumns: PropTypes.array,
@@ -134,11 +136,16 @@ RuleImplementationsTable.propTypes = {
134
136
 
135
137
  const mapStateToProps = (state, props) => ({
136
138
  columns: getRuleImplementationColumns(state),
137
- ruleImplementations: _.getOr(
139
+ ruleImplementations:
140
+ props.ruleImplementations ||
141
+ (state?.structure?.implementations &&
142
+ _.map(({ implementation, type }) => ({
143
+ ...implementation,
144
+ implementation_structure_structure_type: type,
145
+ }))(state.structure.implementations)) ||
138
146
  state.ruleImplementations,
139
- "ruleImplementations"
140
- )(props),
141
147
  ruleImplementationsLoading: state.ruleImplementationsLoading,
148
+ structure: state.structure,
142
149
  implementationsSort: _.path("ruleImplementationQuery.sort")(state),
143
150
  });
144
151
 
@@ -15,6 +15,8 @@ import {
15
15
  RULE_IMPLEMENTATION_EVENTS,
16
16
  IMPLEMENTATION_CONCEPT_LINKS,
17
17
  IMPLEMENTATION_CONCEPT_LINKS_NEW,
18
+ IMPLEMENTATION_STRUCTURES,
19
+ IMPLEMENTATION_STRUCTURES_NEW,
18
20
  RULE_IMPLEMENTATION_MOVE,
19
21
  RULE_IMPLEMENTATION_NEW,
20
22
  RULE_IMPLEMENTATION_RESULT_DETAILS,
@@ -30,18 +32,20 @@ import {
30
32
  } from "../selectors";
31
33
  import EditRule from "./EditRule";
32
34
  import ExecutionDetails from "./ExecutionDetails";
35
+ import ImplementationStructures from "./ImplementationStructures";
36
+ import ImplementationStructuresNew from "./ImplementationStructuresNew";
33
37
  import MoveImplementation from "./MoveImplementation";
34
38
  import NewRule from "./NewRule";
35
39
  import NewRuleImplementation from "./NewRuleImplementation";
36
40
  import Rule from "./Rule";
37
41
  import RuleCrumbs from "./RuleCrumbs";
38
42
  import RuleEvents from "./RuleEvents";
43
+ import RuleFormImplementations from "./RuleFormImplementations";
39
44
  import RuleImplementation from "./RuleImplementation";
40
45
  import RuleImplementationEvents from "./RuleImplementationEvents";
41
46
  import RuleImplementationLoader from "./RuleImplementationLoader";
42
47
  import RuleImplementationProperties from "./RuleImplementationProperties";
43
48
  import RuleImplementationResults from "./RuleImplementationResults";
44
- import RuleFormImplementations from "./RuleFormImplementations";
45
49
  import RuleImplementationsFromRuleLoader from "./RuleImplementationsFromRuleLoader";
46
50
  import RuleLoader from "./RuleLoader";
47
51
  import RuleProperties from "./RuleProperties";
@@ -353,6 +357,38 @@ const ImplementationRoutes = ({
353
357
  </>
354
358
  )}
355
359
  />
360
+ <Route
361
+ exact
362
+ path={IMPLEMENTATION_STRUCTURES}
363
+ render={() => (
364
+ <>
365
+ <RuleCrumbs />
366
+ <Segment>
367
+ {ruleLoaded && ruleImplementationLoaded && (
368
+ <RuleImplementation>
369
+ <ImplementationStructures />
370
+ </RuleImplementation>
371
+ )}
372
+ </Segment>
373
+ </>
374
+ )}
375
+ />
376
+ <Route
377
+ exact
378
+ path={IMPLEMENTATION_STRUCTURES_NEW}
379
+ render={() => (
380
+ <>
381
+ <RuleCrumbs />
382
+ <Segment>
383
+ {ruleLoaded && ruleImplementationLoaded && (
384
+ <RuleImplementation>
385
+ <ImplementationStructuresNew />
386
+ </RuleImplementation>
387
+ )}
388
+ </Segment>
389
+ </>
390
+ )}
391
+ />
356
392
  <Route
357
393
  exact
358
394
  path={RULE_IMPLEMENTATION_RESULTS}
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { ImplementationStructureDelete } from "../ImplementationStructureDelete";
4
+
5
+ describe("<ImplementationStructureDelete />", () => {
6
+ const renderOpts = {
7
+ messages: {
8
+ en: {
9
+ "implementationsStructures.delete.confirmation.header": "header",
10
+ "implementationsStructures.delete.confirmation.content": "content",
11
+ },
12
+ },
13
+ };
14
+
15
+ it("matches the latest snapshot", () => {
16
+ const props = {
17
+ implementationId: 1,
18
+ dataStructureId: 2,
19
+ deleteImplementationStructure: () => {},
20
+ };
21
+ const { container } = render(
22
+ <ImplementationStructureDelete {...props} />,
23
+ renderOpts
24
+ );
25
+ expect(container).toMatchSnapshot();
26
+ });
27
+ });
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { ImplementationStructureLink } from "../ImplementationStructureLink";
4
+
5
+ describe("<ImplementationStructureLink />", () => {
6
+ const renderOpts = {
7
+ messages: {
8
+ en: {},
9
+ },
10
+ };
11
+
12
+ it("matches the latest snapshot", () => {
13
+ const props = {
14
+ id: 1,
15
+ current_version: { name: "foo" },
16
+ };
17
+ const { container } = render(
18
+ <ImplementationStructureLink {...props} />,
19
+ renderOpts
20
+ );
21
+ expect(container).toMatchSnapshot();
22
+ });
23
+ });
@@ -0,0 +1,88 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { ImplementationStructures } from "../ImplementationStructures";
4
+
5
+ describe("<ImplementationStructures />", () => {
6
+ const renderOpts = {
7
+ messages: {
8
+ en: {
9
+ "structure.name": "name",
10
+ "structure.system": "system",
11
+ "structure.path": "path",
12
+ "implementationsStructures.type": "type",
13
+ "links.actions.create": "create",
14
+ "implementationStructures.empty": "empty",
15
+ "implementationsStructures.type.dataset": "dataset",
16
+ },
17
+ },
18
+ };
19
+
20
+ it("matches the latest snapshot", () => {
21
+ const props = {
22
+ implementation: {
23
+ data_structures: [
24
+ {
25
+ data_structure: {
26
+ id: 1,
27
+ name: "foo",
28
+ system: { name: "bar" },
29
+ current_version: {
30
+ name: "qux",
31
+ path: ["baz"],
32
+ },
33
+ },
34
+ type: "dataset",
35
+ },
36
+ ],
37
+ },
38
+ canCreateLink: true,
39
+ };
40
+ const { container } = render(
41
+ <ImplementationStructures {...props} />,
42
+ renderOpts
43
+ );
44
+ expect(container).toMatchSnapshot();
45
+ });
46
+
47
+ it("matches the latest snapshot without create permission", () => {
48
+ const props = {
49
+ implementation: {
50
+ data_structures: [
51
+ {
52
+ data_structure: {
53
+ id: 1,
54
+ name: "foo",
55
+ system: { name: "bar" },
56
+ path: ["baz"],
57
+ current_version: {
58
+ name: "qux",
59
+ path: ["baz"],
60
+ },
61
+ },
62
+ type: "dataset",
63
+ },
64
+ ],
65
+ },
66
+ canCreateLink: false,
67
+ };
68
+ const { container } = render(
69
+ <ImplementationStructures {...props} />,
70
+ renderOpts
71
+ );
72
+ expect(container).toMatchSnapshot();
73
+ });
74
+
75
+ it("matches the latest snapshot without data_structures", () => {
76
+ const props = {
77
+ implementation: {
78
+ data_structures: [],
79
+ },
80
+ canCreateLink: true,
81
+ };
82
+ const { container } = render(
83
+ <ImplementationStructures {...props} />,
84
+ renderOpts
85
+ );
86
+ expect(container).toMatchSnapshot();
87
+ });
88
+ });