@truedat/core 4.44.1 → 4.44.4

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 (35) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +3 -3
  3. package/src/api/queries.js +12 -0
  4. package/src/components/CatalogMenu.js +4 -3
  5. package/src/components/DashboardMenu.js +12 -4
  6. package/src/components/QualityMenu.js +15 -7
  7. package/src/components/SideMenu.js +11 -5
  8. package/src/components/SidebarToggle.js +9 -4
  9. package/src/components/Submenu.js +12 -10
  10. package/src/components/TemplateSelector.js +89 -0
  11. package/src/components/__tests__/DashboardMenu.spec.js +13 -14
  12. package/src/components/__tests__/QualityMenu.spec.js +14 -14
  13. package/src/components/__tests__/SideMenu.spec.js +7 -14
  14. package/src/components/__tests__/Submenu.spec.js +7 -19
  15. package/src/components/__tests__/TemplateSelector.spec.js +51 -0
  16. package/src/components/__tests__/__snapshots__/AdminMenu.spec.js.snap +1 -1
  17. package/src/components/__tests__/__snapshots__/CatalogMenu.spec.js.snap +13 -0
  18. package/src/components/__tests__/__snapshots__/DashboardMenu.spec.js.snap +1 -16
  19. package/src/components/__tests__/__snapshots__/GlossaryMenu.spec.js.snap +1 -1
  20. package/src/components/__tests__/__snapshots__/GrantMenu.spec.js.snap +1 -1
  21. package/src/components/__tests__/__snapshots__/IngestMenu.spec.js.snap +1 -1
  22. package/src/components/__tests__/__snapshots__/LineageMenu.spec.js.snap +1 -1
  23. package/src/components/__tests__/__snapshots__/MembersMenu.spec.js.snap +1 -1
  24. package/src/components/__tests__/__snapshots__/QualityMenu.spec.js.snap +60 -26
  25. package/src/components/__tests__/__snapshots__/SearchMenu.spec.js.snap +1 -1
  26. package/src/components/__tests__/__snapshots__/SideMenu.spec.js.snap +58 -30
  27. package/src/components/__tests__/__snapshots__/Submenu.spec.js.snap +30 -38
  28. package/src/components/__tests__/__snapshots__/TaxonomyMenu.spec.js.snap +1 -1
  29. package/src/components/__tests__/__snapshots__/TemplateSelector.spec.js.snap +76 -0
  30. package/src/components/index.js +2 -0
  31. package/src/messages/en.js +5 -0
  32. package/src/messages/es.js +5 -0
  33. package/src/routes.js +21 -18
  34. package/src/services/file.js +9 -0
  35. package/src/services/sort.js +2 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.44.4] 2022-05-19
4
+
5
+ ### Added
6
+
7
+ - [TD-4660] `lowerDeburrTrim` function, reference data routes and i18n messages
8
+
3
9
  ## [4.44.1] 2022-05-11
4
10
 
5
11
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "4.44.1",
3
+ "version": "4.44.4",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -32,7 +32,7 @@
32
32
  "@babel/plugin-transform-modules-commonjs": "^7.15.0",
33
33
  "@babel/preset-env": "^7.15.0",
34
34
  "@babel/preset-react": "^7.14.5",
35
- "@truedat/test": "4.44.1",
35
+ "@truedat/test": "4.44.4",
36
36
  "babel-jest": "^27.0.6",
37
37
  "babel-plugin-dynamic-import-node": "^2.3.3",
38
38
  "babel-plugin-lodash": "^3.3.4",
@@ -106,5 +106,5 @@
106
106
  "react-dom": ">= 16.8.6 < 17",
107
107
  "semantic-ui-react": ">= 0.88.2 < 2.1"
108
108
  },
109
- "gitHead": "20d38381ed4cb6658d1122a4bff08a13fedc2d59"
109
+ "gitHead": "36fb183e1d22181e6a15b3bac2c01b39214eacdb"
110
110
  }
@@ -9,3 +9,15 @@ export const DOMAINS_QUERY = gql`
9
9
  }
10
10
  }
11
11
  `;
12
+
13
+ export const TEMPLATES_QUERY = gql`
14
+ query Templates($scope: String!) {
15
+ templates(scope: $scope) {
16
+ id
17
+ name
18
+ label
19
+ scope
20
+ content
21
+ }
22
+ }
23
+ `;
@@ -1,12 +1,12 @@
1
- import _ from "lodash/fp";
2
1
  import React from "react";
3
2
  import { useAuthorized } from "../hooks";
4
3
  import {
5
- STRUCTURES_UPLOAD_EVENTS,
6
4
  PENDING_STRUCTURE_NOTES,
5
+ REFERENCE_DATASETS,
6
+ STRUCTURES,
7
+ STRUCTURES_UPLOAD_EVENTS,
7
8
  STRUCTURE_TAGS,
8
9
  STRUCTURE_TYPES,
9
- STRUCTURES,
10
10
  SYSTEMS,
11
11
  } from "../routes";
12
12
  import Submenu from "./Submenu";
@@ -16,6 +16,7 @@ const items = [{ name: "structures", routes: [STRUCTURES, SYSTEMS] }];
16
16
  const adminItems = [
17
17
  { name: "structureTypes", routes: [STRUCTURE_TYPES] },
18
18
  { name: "structureTags", routes: [STRUCTURE_TAGS] },
19
+ { name: "referenceData", routes: [REFERENCE_DATASETS] },
19
20
  ];
20
21
 
21
22
  const structureNoteItems = [
@@ -1,6 +1,7 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
- import { useSelector } from "react-redux";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
4
5
  import { DASHBOARD } from "@truedat/core/routes";
5
6
  import { getDashboardConfig } from "../selectors";
6
7
  import { useAuthorized } from "../hooks";
@@ -8,9 +9,8 @@ import Submenu from "./Submenu";
8
9
 
9
10
  const items = [{ name: "dashboard", routes: [DASHBOARD] }];
10
11
 
11
- export const DashboardMenu = () => {
12
+ export const DashboardMenu = ({ dashboardConfig }) => {
12
13
  const authorized = useAuthorized("dashboards");
13
- const dashboardConfig = useSelector(getDashboardConfig);
14
14
 
15
15
  return authorized && !_.isEmpty(dashboardConfig) ? (
16
16
  <Submenu items={items} icon="chart line" name="dashboard" />
@@ -19,4 +19,12 @@ export const DashboardMenu = () => {
19
19
  );
20
20
  };
21
21
 
22
- export default DashboardMenu;
22
+ DashboardMenu.propTypes = {
23
+ dashboardConfig: PropTypes.object,
24
+ };
25
+
26
+ export const mapStateToProps = (state) => ({
27
+ dashboardConfig: getDashboardConfig(state),
28
+ });
29
+
30
+ export default connect(mapStateToProps)(DashboardMenu);
@@ -1,28 +1,36 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
- import { useSelector } from "react-redux";
3
+ import PropTypes from "prop-types";
4
+ import { connect } from "react-redux";
4
5
  import { useAuthorized } from "../hooks";
5
6
  import { RULES, QUALITY_DASHBOARD, IMPLEMENTATIONS } from "../routes";
6
7
  import { getQualityDashboardConfig } from "../selectors";
7
8
  import Submenu from "./Submenu";
8
9
 
9
- const items = [
10
+ export const ITEMS = [
10
11
  { name: "rules", routes: [RULES] },
11
12
  { name: "implementations", routes: [IMPLEMENTATIONS] },
12
- { name: "quality_dashboard", routes: [QUALITY_DASHBOARD] }
13
+ { name: "quality_dashboard", routes: [QUALITY_DASHBOARD] },
13
14
  ];
14
15
 
15
- export const QualityMenu = () => {
16
+ export const QualityMenu = ({ dashboardConfig }) => {
16
17
  const authorized = useAuthorized("data_quality");
17
- const dashboardConfig = useSelector(getQualityDashboardConfig);
18
18
 
19
19
  const filteredItems = _.filter(
20
20
  ({ name }) => name != "quality_dashboard" || !_.isEmpty(dashboardConfig)
21
- )(items);
21
+ )(ITEMS);
22
22
 
23
23
  return authorized ? (
24
24
  <Submenu items={filteredItems} icon="check square" name="quality" />
25
25
  ) : null;
26
26
  };
27
27
 
28
- export default QualityMenu;
28
+ QualityMenu.propTypes = {
29
+ dashboardConfig: PropTypes.object,
30
+ };
31
+
32
+ export const mapStateToProps = (state) => ({
33
+ dashboardConfig: getQualityDashboardConfig(state),
34
+ });
35
+
36
+ export default connect(mapStateToProps)(QualityMenu);
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import PropTypes from "prop-types";
3
- import { useSelector } from "react-redux";
3
+ import { connect } from "react-redux";
4
4
  import { Menu, Sidebar } from "semantic-ui-react";
5
5
  import AdminMenu from "./AdminMenu";
6
6
  import CatalogMenu from "./CatalogMenu";
@@ -15,9 +15,12 @@ import TaxonomyMenu from "./TaxonomyMenu";
15
15
  import GrantMenu from "./GrantMenu";
16
16
  import MembersMenu from "./MembersMenu";
17
17
 
18
- export const SideMenu = ({ children, animation = "push", inverted }) => {
19
- const sidebarVisible = useSelector((state) => state.sidebarVisible);
20
-
18
+ export const SideMenu = ({
19
+ animation = "push",
20
+ children,
21
+ inverted,
22
+ sidebarVisible,
23
+ }) => {
21
24
  const width = sidebarVisible ? null : "very thin";
22
25
 
23
26
  return (
@@ -53,6 +56,9 @@ SideMenu.propTypes = {
53
56
  animation: PropTypes.string,
54
57
  children: PropTypes.node,
55
58
  inverted: PropTypes.bool,
59
+ sidebarVisible: PropTypes.bool,
56
60
  };
57
61
 
58
- export default SideMenu;
62
+ export const mapStateToProps = ({ sidebarVisible }) => ({ sidebarVisible });
63
+
64
+ export default connect(mapStateToProps)(SideMenu);
@@ -1,18 +1,18 @@
1
1
  import React, { useEffect } from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import { FormattedMessage } from "react-intl";
4
- import { connect, useSelector } from "react-redux";
4
+ import { connect } from "react-redux";
5
5
  import { useLocation } from "react-router-dom";
6
6
  import { Icon, Menu } from "semantic-ui-react";
7
7
  import { hideSidebar, toggleSidebar } from "@truedat/core/routines";
8
8
 
9
9
  export const SidebarToggle = ({
10
+ animation = "push",
10
11
  hideSidebar,
12
+ sidebarVisible,
11
13
  toggleSidebar,
12
- animation = "push",
13
14
  }) => {
14
15
  const location = useLocation();
15
- const sidebarVisible = useSelector((state) => state.sidebarVisible);
16
16
  useEffect(() => {
17
17
  if (animation == "overlay") {
18
18
  hideSidebar();
@@ -45,6 +45,11 @@ SidebarToggle.propTypes = {
45
45
  animation: PropTypes.string,
46
46
  hideSidebar: PropTypes.func,
47
47
  toggleSidebar: PropTypes.func,
48
+ sidebarVisible: PropTypes.bool,
48
49
  };
49
50
 
50
- export default connect(null, { hideSidebar, toggleSidebar })(SidebarToggle);
51
+ export const mapStateToProps = ({ sidebarVisible }) => ({ sidebarVisible });
52
+
53
+ export default connect(mapStateToProps, { hideSidebar, toggleSidebar })(
54
+ SidebarToggle
55
+ );
@@ -2,7 +2,7 @@ import _ from "lodash/fp";
2
2
  import React, { useEffect, useState } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { useIntl, FormattedMessage } from "react-intl";
5
- import { useSelector } from "react-redux";
5
+ import { connect } from "react-redux";
6
6
  import { useLocation, useHistory, Link } from "react-router-dom";
7
7
  import { Dropdown, Icon, Menu } from "semantic-ui-react";
8
8
  import { useAuthorizedItems, useActiveRoutes } from "@truedat/core/hooks";
@@ -19,14 +19,14 @@ export const MenuItem = ({ name, routes }) => {
19
19
  to={primaryRoute}
20
20
  active={active}
21
21
  content={formatMessage({ id: `sidemenu.${name}`, defaultMessage: name })}
22
- onClick={e => e.stopPropagation()}
22
+ onClick={(e) => e.stopPropagation()}
23
23
  />
24
24
  );
25
25
  };
26
26
 
27
27
  MenuItem.propTypes = {
28
28
  name: PropTypes.string,
29
- routes: PropTypes.array
29
+ routes: PropTypes.array,
30
30
  };
31
31
 
32
32
  export const DropdownItem = ({ name, routes }) => {
@@ -40,19 +40,18 @@ export const DropdownItem = ({ name, routes }) => {
40
40
  to={primaryRoute}
41
41
  active={active}
42
42
  content={formatMessage({ id: `sidemenu.${name}`, defaultMessage: name })}
43
- onClick={e => e.stopPropagation()}
43
+ onClick={(e) => e.stopPropagation()}
44
44
  />
45
45
  );
46
46
  };
47
47
 
48
48
  DropdownItem.propTypes = {
49
49
  name: PropTypes.string,
50
- routes: PropTypes.array
50
+ routes: PropTypes.array,
51
51
  };
52
52
 
53
- export const Submenu = ({ items, icon, name }) => {
53
+ export const Submenu = ({ items, icon, name, sidebarVisible }) => {
54
54
  const [open, setOpen] = useState(false);
55
- const sidebarVisible = useSelector(state => state.sidebarVisible);
56
55
  const active = useActiveRoutes(_.flatMap("routes")(items));
57
56
  const filteredItems = useAuthorizedItems(items);
58
57
  const location = useLocation();
@@ -107,7 +106,7 @@ export const Submenu = ({ items, icon, name }) => {
107
106
  trigger={trigger}
108
107
  onClick={() => history.push(primaryRoute)}
109
108
  >
110
- <Dropdown.Menu onClick={e => e.stopPropagation()}>
109
+ <Dropdown.Menu onClick={(e) => e.stopPropagation()}>
111
110
  {_.size(filteredItems) > 1 && (
112
111
  <>
113
112
  <Dropdown.Header
@@ -132,7 +131,10 @@ export const Submenu = ({ items, icon, name }) => {
132
131
 
133
132
  Submenu.propTypes = {
134
133
  items: PropTypes.array,
135
- icon: PropTypes.string
134
+ icon: PropTypes.string,
135
+ sidebarVisible: PropTypes.bool,
136
136
  };
137
137
 
138
- export default Submenu;
138
+ export const mapStateToProps = ({ sidebarVisible }) => ({ sidebarVisible });
139
+
140
+ export default connect(mapStateToProps)(Submenu);
@@ -0,0 +1,89 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Form, Label } from "semantic-ui-react";
5
+ import { useIntl, FormattedMessage } from "react-intl";
6
+ import { useQuery } from "@apollo/client";
7
+ import { accentInsensitivePathOrder } from "../services/sort";
8
+ import { TEMPLATES_QUERY } from "../api/queries";
9
+
10
+ const makeOptions = _.flow(
11
+ _.map(({ label: text, id: value }) => ({
12
+ key: value,
13
+ value,
14
+ text,
15
+ })),
16
+ _.sortBy(accentInsensitivePathOrder("text"))
17
+ );
18
+
19
+ export const TemplateSelector = ({
20
+ clearable,
21
+ loading,
22
+ name = "template",
23
+ onChange,
24
+ required,
25
+ selectedValue,
26
+ templates,
27
+ }) => {
28
+ const { formatMessage } = useIntl();
29
+ const options = makeOptions(templates);
30
+ const hidden = _.size(templates) <= 1;
31
+
32
+ const handleChange = (e, { value, ...data }) => {
33
+ const template = _.find({ id: value })(templates);
34
+ onChange(e, { ...data, value, template });
35
+ };
36
+
37
+ return hidden ? null : (
38
+ <Form.Field required={required}>
39
+ <label>
40
+ <FormattedMessage id="template.selector.label" />
41
+ {!selectedValue && required ? (
42
+ <Label pointing="left">
43
+ <FormattedMessage id="template.form.validation.empty_required" />
44
+ </Label>
45
+ ) : null}
46
+ </label>
47
+ <Form.Dropdown
48
+ clearable={clearable}
49
+ loading={loading}
50
+ name={name}
51
+ onChange={handleChange}
52
+ options={options}
53
+ placeholder={formatMessage({ id: "template.selector.placeholder" })}
54
+ search
55
+ selection
56
+ value={_.toString(selectedValue)}
57
+ />
58
+ </Form.Field>
59
+ );
60
+ };
61
+
62
+ TemplateSelector.propTypes = {
63
+ clearable: PropTypes.bool,
64
+ loading: PropTypes.bool,
65
+ onChange: PropTypes.func,
66
+ name: PropTypes.string,
67
+ required: PropTypes.bool,
68
+ selectedValue: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
69
+ templates: PropTypes.array,
70
+ };
71
+
72
+ export const TemplateSelectorLoader = ({ scope, onLoad, ...props }) => {
73
+ const { loading, error, data } = useQuery(TEMPLATES_QUERY, {
74
+ variables: { scope },
75
+ onCompleted: onLoad,
76
+ });
77
+ if (error) return null;
78
+ const templates = data?.templates || [];
79
+ return (
80
+ <TemplateSelector loading={loading} templates={templates} {...props} />
81
+ );
82
+ };
83
+
84
+ TemplateSelectorLoader.propTypes = {
85
+ scope: PropTypes.string.isRequired,
86
+ onLoad: PropTypes.func,
87
+ };
88
+
89
+ export default TemplateSelectorLoader;
@@ -1,15 +1,14 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
3
- import { DashboardMenu } from "..";
2
+ import { render } from "@truedat/test/render";
3
+ import DashboardMenu from "../DashboardMenu";
4
4
 
5
5
  jest.mock("../../hooks", () => ({
6
6
  useActiveRoutes: jest.fn(() => true),
7
- useAuthorized: jest.fn(() => true)
7
+ useAuthorized: jest.fn(() => true),
8
8
  }));
9
9
 
10
- jest.mock("react-redux", () => ({
11
- ...jest.requireActual("react-redux"),
12
- useSelector: jest.fn(() => ({
10
+ const renderOpts = {
11
+ state: {
13
12
  systemConfiguration: [
14
13
  {
15
14
  external_id: "config_metabase",
@@ -18,16 +17,16 @@ jest.mock("react-redux", () => ({
18
17
  dashboard_id: 2,
19
18
  quality_dashboard_id: 8,
20
19
  metabase_url: "http://metabase.com",
21
- secret_key: "###"
22
- }
23
- }
24
- ]
25
- }))
26
- }));
20
+ secret_key: "###",
21
+ },
22
+ },
23
+ ],
24
+ },
25
+ };
27
26
 
28
27
  describe("<DashboardMenu />", () => {
29
28
  it("matches the latest snapshot", () => {
30
- const wrapper = shallow(<DashboardMenu />);
31
- expect(wrapper).toMatchSnapshot();
29
+ const { container } = render(<DashboardMenu />, renderOpts);
30
+ expect(container).toMatchSnapshot();
32
31
  });
33
32
  });
@@ -1,15 +1,15 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
3
- import { QualityMenu } from "../QualityMenu";
2
+ import { render } from "@truedat/test/render";
3
+ import QualityMenu from "../QualityMenu";
4
4
 
5
5
  jest.mock("../../hooks", () => ({
6
6
  useActiveRoutes: jest.fn(() => true),
7
- useAuthorized: jest.fn(() => true)
7
+ useAuthorized: jest.fn(() => true),
8
+ useAuthorizedItems: jest.fn((items) => items),
8
9
  }));
9
10
 
10
- jest.mock("react-redux", () => ({
11
- ...jest.requireActual("react-redux"),
12
- useSelector: jest.fn(() => ({
11
+ const renderOpts = {
12
+ state: {
13
13
  systemConfiguration: [
14
14
  {
15
15
  external_id: "config_metabase",
@@ -18,16 +18,16 @@ jest.mock("react-redux", () => ({
18
18
  dashboard_id: 2,
19
19
  quality_dashboard_id: 8,
20
20
  metabase_url: "http://metabase.com",
21
- secret_key: "###"
22
- }
23
- }
24
- ]
25
- }))
26
- }));
21
+ secret_key: "###",
22
+ },
23
+ },
24
+ ],
25
+ },
26
+ };
27
27
 
28
28
  describe("<QualityMenu />", () => {
29
29
  it("matches the latest snapshot", () => {
30
- const wrapper = shallow(<QualityMenu />);
31
- expect(wrapper).toMatchSnapshot();
30
+ const { container } = render(<QualityMenu />, renderOpts);
31
+ expect(container).toMatchSnapshot();
32
32
  });
33
33
  });
@@ -1,24 +1,17 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
3
- import { SideMenu } from "../SideMenu";
2
+ import { render } from "@truedat/test/render";
3
+ import SideMenu from "../SideMenu";
4
4
 
5
- jest.mock("@fluentui/react-component-event-listener", () => ({
6
- documentRef: null,
7
- windowRef: null
8
- }));
9
-
10
- jest.mock("react-redux", () => ({
11
- ...jest.requireActual("react-redux"),
12
- useSelector: jest.fn(() => ({ sidebarVisible: true }))
13
- }));
5
+ const renderOpts = { state: { sidebarVisible: true } };
14
6
 
15
7
  describe("<SideMenu />", () => {
16
8
  it("matches the latest snapshot", () => {
17
- const wrapper = shallow(
9
+ const { container } = render(
18
10
  <SideMenu>
19
11
  <p>Hello</p>
20
- </SideMenu>
12
+ </SideMenu>,
13
+ renderOpts
21
14
  );
22
- expect(wrapper).toMatchSnapshot();
15
+ expect(container).toMatchSnapshot();
23
16
  });
24
17
  });
@@ -1,29 +1,17 @@
1
1
  import React from "react";
2
- import { shallow } from "enzyme";
3
- import { Submenu } from "..";
2
+ import { render } from "@truedat/test/render";
3
+ import Submenu from "../Submenu";
4
4
 
5
- const mockHistory = {
6
- push: jest.fn()
7
- };
8
-
9
- jest.mock("react-redux", () => ({
10
- ...jest.requireActual("react-redux"),
11
- useSelector: jest.fn(() => ({ sidebarVisible: true }))
12
- }));
13
-
14
- jest.mock("react-router-dom", () => ({
15
- ...jest.requireActual("react-router-dom"),
16
- useLocation: () => ({ pathname: "/bar" }),
17
- useHistory: () => mockHistory
18
- }));
5
+ const renderOpts = { state: { sidebarVisible: true }, routes: ["/bar"] };
19
6
 
20
7
  describe("<Submenu />", () => {
21
8
  it("matches the latest snapshot", () => {
22
9
  const items = [
23
10
  { name: "foo", routes: ["/foo", "/bar"] },
24
- { name: "baz", routes: ["/baz"] }
11
+ { name: "baz", routes: ["/baz"] },
25
12
  ];
26
- const wrapper = shallow(<Submenu items={items} />);
27
- expect(wrapper).toMatchSnapshot();
13
+ const props = { name: "foo", items };
14
+ const { container } = render(<Submenu {...props} />, renderOpts);
15
+ expect(container).toMatchSnapshot();
28
16
  });
29
17
  });
@@ -0,0 +1,51 @@
1
+ import React, { Suspense } from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import { render } from "@truedat/test/render";
4
+ import { TEMPLATES_QUERY } from "@truedat/core/api/queries";
5
+ import TemplateSelector from "../TemplateSelector";
6
+
7
+ const scope = "foo";
8
+ const templates = [
9
+ {
10
+ id: "1",
11
+ name: "template1",
12
+ label: "template1",
13
+ content: [{ name: "g1", fields: [{ name: "field1", label: "field1" }] }],
14
+ scope,
15
+ },
16
+ {
17
+ id: "2",
18
+ name: "template2",
19
+ label: "template2",
20
+ content: {},
21
+ scope,
22
+ },
23
+ ];
24
+ const templatesMock = {
25
+ request: { query: TEMPLATES_QUERY, variables: { scope } },
26
+ result: { data: { templates } },
27
+ };
28
+
29
+ const renderOpts = {
30
+ mocks: [templatesMock],
31
+ };
32
+
33
+ describe("<TemplateSelector />", () => {
34
+ const onChange = jest.fn();
35
+ const onLoad = jest.fn();
36
+ const props = { onChange, onLoad, scope: "foo" };
37
+
38
+ it("matches the latest snapshot", async () => {
39
+ const { container, queryByText } = render(
40
+ <Suspense fallback="loading...">
41
+ <TemplateSelector {...props} />
42
+ </Suspense>,
43
+ renderOpts
44
+ );
45
+ await waitFor(() => {
46
+ expect(queryByText(/template1/i)).toBeInTheDocument();
47
+ });
48
+ expect(container).toMatchSnapshot();
49
+ expect(onLoad.mock.calls.length).toBe(1);
50
+ });
51
+ });
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<AdminMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="setting"
6
6
  items={
7
7
  Array [
@@ -68,6 +68,19 @@ exports[`<CatalogMenu /> matches the latest snapshot 1`] = `
68
68
  structureTags
69
69
  </span>
70
70
  </a>
71
+ <a
72
+ aria-checked="false"
73
+ class="item"
74
+ href="/referenceDatasets"
75
+ name="referenceData"
76
+ role="option"
77
+ >
78
+ <span
79
+ class="text"
80
+ >
81
+ referenceData
82
+ </span>
83
+ </a>
71
84
  <a
72
85
  aria-checked="false"
73
86
  class="item"
@@ -1,18 +1,3 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`<DashboardMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
5
- icon="chart line"
6
- items={
7
- Array [
8
- Object {
9
- "name": "dashboard",
10
- "routes": Array [
11
- "/dashboard",
12
- ],
13
- },
14
- ]
15
- }
16
- name="dashboard"
17
- />
18
- `;
3
+ exports[`<DashboardMenu /> matches the latest snapshot 1`] = `<div />`;
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<GlossaryMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="tags"
6
6
  items={
7
7
  Array [
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<GrantMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="key"
6
6
  items={
7
7
  Array [
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<IngestMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="hdd outline"
6
6
  items={
7
7
  Array [
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<LineageMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="shuffle"
6
6
  items={
7
7
  Array [
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<MembersMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="users"
6
6
  items={
7
7
  Array [
@@ -1,30 +1,64 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<QualityMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
5
- icon="check square"
6
- items={
7
- Array [
8
- Object {
9
- "name": "rules",
10
- "routes": Array [
11
- "/rules",
12
- ],
13
- },
14
- Object {
15
- "name": "implementations",
16
- "routes": Array [
17
- "/implementations",
18
- ],
19
- },
20
- Object {
21
- "name": "quality_dashboard",
22
- "routes": Array [
23
- "/quality_dashboard",
24
- ],
25
- },
26
- ]
27
- }
28
- name="quality"
29
- />
4
+ <div>
5
+ <div
6
+ class="active"
7
+ >
8
+ <div
9
+ aria-expanded="false"
10
+ class="ui item dropdown active"
11
+ role="listbox"
12
+ tabindex="0"
13
+ >
14
+ <a
15
+ class="ui"
16
+ href="/rules"
17
+ >
18
+ <i
19
+ aria-hidden="true"
20
+ class="check square large icon"
21
+ />
22
+ </a>
23
+ <div
24
+ class="menu transition"
25
+ >
26
+ <div
27
+ class="header selectable"
28
+ >
29
+ Data Quality
30
+ </div>
31
+ <div
32
+ class="divider"
33
+ />
34
+ <a
35
+ aria-checked="true"
36
+ class="active item"
37
+ href="/rules"
38
+ name="rules"
39
+ role="option"
40
+ >
41
+ <span
42
+ class="text"
43
+ >
44
+ Quality Rules
45
+ </span>
46
+ </a>
47
+ <a
48
+ aria-checked="true"
49
+ class="active item"
50
+ href="/implementations"
51
+ name="implementations"
52
+ role="option"
53
+ >
54
+ <span
55
+ class="text"
56
+ >
57
+ Implementations
58
+ </span>
59
+ </a>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
30
64
  `;
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<SearchMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="search"
6
6
  items={
7
7
  Array [
@@ -1,38 +1,66 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<SideMenu /> matches the latest snapshot 1`] = `
4
- <SidebarPushable>
5
- <Sidebar
6
- animation="push"
7
- as={[Function]}
8
- direction="left"
9
- icon={false}
10
- target={null}
11
- vertical={true}
12
- visible={true}
13
- width={null}
4
+ <div>
5
+ <div
6
+ class="pushable"
14
7
  >
15
- <SearchMenu />
16
- <GlossaryMenu />
17
- <CatalogMenu />
18
- <GrantMenu />
19
- <QualityMenu />
20
- <LineageMenu />
21
- <IngestMenu />
22
- <DashboardMenu />
23
- <TaxonomyMenu />
24
- <MembersMenu />
25
- <AdminMenu />
26
- <Connect(SidebarToggle)
27
- animation="push"
28
- />
29
- </Sidebar>
30
- <SidebarPusher
31
- content={
8
+ <div
9
+ class="ui vertical ui push left visible sidebar menu"
10
+ >
11
+ <div>
12
+ <div
13
+ aria-expanded="false"
14
+ class="ui item dropdown"
15
+ role="listbox"
16
+ tabindex="0"
17
+ >
18
+ <a
19
+ class="ui"
20
+ href="/search"
21
+ >
22
+ <i
23
+ aria-hidden="true"
24
+ class="search large icon"
25
+ />
26
+ Search
27
+ </a>
28
+ <div
29
+ class="menu transition"
30
+ >
31
+ <a
32
+ aria-checked="false"
33
+ class="item"
34
+ href="/search"
35
+ name="search"
36
+ role="option"
37
+ >
38
+ <span
39
+ class="text"
40
+ >
41
+ Search
42
+ </span>
43
+ </a>
44
+ </div>
45
+ </div>
46
+ </div>
47
+ <a
48
+ class="item bottom"
49
+ >
50
+ <i
51
+ aria-hidden="true"
52
+ class="angle double left large icon"
53
+ />
54
+ Collapse sidebar
55
+ </a>
56
+ </div>
57
+ <div
58
+ class="pusher"
59
+ >
32
60
  <p>
33
61
  Hello
34
62
  </p>
35
- }
36
- />
37
- </SidebarPushable>
63
+ </div>
64
+ </div>
65
+ </div>
38
66
  `;
@@ -1,43 +1,35 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<Submenu /> matches the latest snapshot 1`] = `
4
- <MenuItem
5
- active={true}
6
- as="div"
7
- className="selectable"
8
- onClick={[Function]}
9
- >
10
- <Link
11
- to="/foo"
4
+ <div>
5
+ <div
6
+ class="active item selectable"
12
7
  >
13
- <Icon
14
- as="i"
15
- size="large"
16
- />
17
- <MemoizedFormattedMessage
18
- id="sidemenu.undefined"
19
- />
20
- </Link>
21
- <MenuMenu>
22
- <MenuItem
23
- key="0"
24
- name="foo"
25
- routes={
26
- Array [
27
- "/foo",
28
- "/bar",
29
- ]
30
- }
31
- />
32
- <MenuItem
33
- key="1"
34
- name="baz"
35
- routes={
36
- Array [
37
- "/baz",
38
- ]
39
- }
40
- />
41
- </MenuMenu>
42
- </MenuItem>
8
+ <a
9
+ href="/foo"
10
+ >
11
+ <i
12
+ aria-hidden="true"
13
+ class="large icon"
14
+ />
15
+ foo
16
+ </a>
17
+ <div
18
+ class="menu"
19
+ >
20
+ <a
21
+ class="active link item"
22
+ href="/foo"
23
+ >
24
+ foo
25
+ </a>
26
+ <a
27
+ class="link item"
28
+ href="/baz"
29
+ >
30
+ baz
31
+ </a>
32
+ </div>
33
+ </div>
34
+ </div>
43
35
  `;
@@ -1,7 +1,7 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<TaxonomyMenu /> matches the latest snapshot 1`] = `
4
- <Submenu
4
+ <Connect(Submenu)
5
5
  icon="sitemap"
6
6
  items={
7
7
  Array [
@@ -0,0 +1,76 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<TemplateSelector /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="field"
7
+ >
8
+ <label>
9
+ Template
10
+ </label>
11
+ <div
12
+ class="field"
13
+ >
14
+ <div
15
+ aria-busy="false"
16
+ aria-expanded="false"
17
+ class="ui search selection dropdown"
18
+ name="template"
19
+ role="combobox"
20
+ >
21
+ <input
22
+ aria-autocomplete="list"
23
+ autocomplete="off"
24
+ class="search"
25
+ tabindex="0"
26
+ type="text"
27
+ value=""
28
+ />
29
+ <div
30
+ aria-atomic="true"
31
+ aria-live="polite"
32
+ class="divider default text"
33
+ role="alert"
34
+ >
35
+ Select a template...
36
+ </div>
37
+ <i
38
+ aria-hidden="true"
39
+ class="dropdown icon"
40
+ />
41
+ <div
42
+ class="menu transition"
43
+ role="listbox"
44
+ >
45
+ <div
46
+ aria-checked="false"
47
+ aria-selected="true"
48
+ class="selected item"
49
+ role="option"
50
+ style="pointer-events: all;"
51
+ >
52
+ <span
53
+ class="text"
54
+ >
55
+ template1
56
+ </span>
57
+ </div>
58
+ <div
59
+ aria-checked="false"
60
+ aria-selected="false"
61
+ class="item"
62
+ role="option"
63
+ style="pointer-events: all;"
64
+ >
65
+ <span
66
+ class="text"
67
+ >
68
+ template2
69
+ </span>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ `;
@@ -37,6 +37,7 @@ import SidebarToggle from "./SidebarToggle";
37
37
  import SideMenu from "./SideMenu";
38
38
  import Submenu from "./Submenu";
39
39
  import TaxonomyMenu from "./TaxonomyMenu";
40
+ import TemplateSelector from "./TemplateSelector";
40
41
  import TreeSelector from "./TreeSelector";
41
42
  import Unauthorized from "./Unauthorized";
42
43
  import UploadModal from "./UploadModal";
@@ -81,6 +82,7 @@ export {
81
82
  SideMenu,
82
83
  Submenu,
83
84
  TaxonomyMenu,
85
+ TemplateSelector,
84
86
  TreeSelector,
85
87
  Unauthorized,
86
88
  UploadModal,
@@ -4,8 +4,12 @@ export default {
4
4
  "actions.confirm": "Are you sure?",
5
5
  "actions.create": "Create",
6
6
  "actions.delete": "Delete",
7
+ "actions.download": "Download",
7
8
  "actions.edit": "Edit",
9
+ "actions.next": "Next",
10
+ "actions.prev": "Previous",
8
11
  "actions.save": "Save",
12
+ "actions.share": "Share",
9
13
  "actions.update": "Update",
10
14
  "alert.createComment.failed.header": "Error creating comment",
11
15
  "alert.fetchComments.failed.header": "Error fetching comments list",
@@ -88,6 +92,7 @@ export default {
88
92
  "sidemenu.pending_structure_notes": "Pending notes",
89
93
  "sidemenu.quality_dashboard": "Quality Dashboard",
90
94
  "sidemenu.quality": "Data Quality",
95
+ "sidemenu.referenceData": "Reference Data",
91
96
  "sidemenu.roles": "Roles",
92
97
  "sidemenu.rules": "Quality Rules",
93
98
  "sidemenu.search": "Search",
@@ -4,8 +4,12 @@ export default {
4
4
  "actions.confirm": "¿Estás seguro?",
5
5
  "actions.create": "Crear",
6
6
  "actions.delete": "Eliminar",
7
+ "actions.download": "Descargar",
7
8
  "actions.edit": "Editar",
9
+ "actions.next": "Siguiente",
10
+ "actions.prev": "Anterior",
8
11
  "actions.save": "Guardar",
12
+ "actions.share": "Compartir",
9
13
  "actions.update": "Actualizar",
10
14
  "alert.createComment.failed.header": "Error creando comentario",
11
15
  "alert.fetchComments.failed.header": "Error cargando la lista de comentarios",
@@ -91,6 +95,7 @@ export default {
91
95
  "sidemenu.pending_structure_notes": "Notas pendientes",
92
96
  "sidemenu.quality_dashboard": "Dashboard de Calidad",
93
97
  "sidemenu.quality": "Calidad",
98
+ "sidemenu.referenceData": "Datos de referencia",
94
99
  "sidemenu.roles": "Roles",
95
100
  "sidemenu.rules": "Reglas",
96
101
  "sidemenu.search": "Búsqueda",
package/src/routes.js CHANGED
@@ -109,6 +109,10 @@ export const PROFILE_EXECUTION =
109
109
  "/profileGroups/:group_id/profileExecutions/:id";
110
110
  export const PROFILE_GROUP = "/profileGroups/:id";
111
111
  export const QUALITY_DASHBOARD = "/quality_dashboard";
112
+ export const REFERENCE_DATASETS = "/referenceDatasets";
113
+ export const REFERENCE_DATASET_EDIT = "/referenceDatasets/:id/edit";
114
+ export const REFERENCE_DATASET_NEW = "/referenceDatasets/new";
115
+ export const REFERENCE_DATASET = "/referenceDatasets/:id";
112
116
  export const REMEDIATION_EDIT =
113
117
  "/rules/:id/implementations/:implementation_id/results/:rule_result_id/remediation/edit";
114
118
  export const REMEDIATION_NEW =
@@ -229,19 +233,23 @@ const routes = {
229
233
  GROUPS,
230
234
  GROUP_CREATE,
231
235
  GROUP_EDIT,
232
- IMPLEMENTATIONS,
233
- IMPLEMENTATION_NEW,
234
- IMPLEMENTATION_NEW_RAW,
235
236
  IMPLEMENTATION,
237
+ IMPLEMENTATIONS,
238
+ IMPLEMENTATION_CLONE,
239
+ IMPLEMENTATION_CONCEPT_LINKS,
240
+ IMPLEMENTATION_CONCEPT_LINKS_NEW,
236
241
  IMPLEMENTATION_EDIT,
237
242
  IMPLEMENTATION_EVENTS,
238
- IMPLEMENTATION_CLONE,
239
243
  IMPLEMENTATION_MOVE,
240
- IMPLEMENTATION_RESULT_DETAILS,
241
- IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
242
- IMPLEMENTATION_RESULT_REMEDIATION_PLAN,
244
+ IMPLEMENTATION_NEW,
245
+ IMPLEMENTATION_NEW_RAW,
243
246
  IMPLEMENTATION_RESULTS,
244
247
  IMPLEMENTATION_RESULTS_DETAILS,
248
+ IMPLEMENTATION_RESULT_DETAILS,
249
+ IMPLEMENTATION_RESULT_REMEDIATION_PLAN,
250
+ IMPLEMENTATION_RESULT_SEGMENTS_RESULTS,
251
+ IMPLEMENTATION_STRUCTURES,
252
+ IMPLEMENTATION_STRUCTURES_NEW,
245
253
  INGEST,
246
254
  INGESTS,
247
255
  INGESTS_NEW,
@@ -266,6 +274,10 @@ const routes = {
266
274
  PROFILE_EXECUTION,
267
275
  PROFILE_GROUP,
268
276
  QUALITY_DASHBOARD,
277
+ REFERENCE_DATASET,
278
+ REFERENCE_DATASETS,
279
+ REFERENCE_DATASET_EDIT,
280
+ REFERENCE_DATASET_NEW,
269
281
  REMEDIATION_EDIT,
270
282
  REMEDIATION_NEW,
271
283
  REMEDIATION_PLAN,
@@ -276,13 +288,9 @@ const routes = {
276
288
  RULES,
277
289
  RULE_EDIT,
278
290
  RULE_EVENTS,
279
- IMPLEMENTATION_CONCEPT_LINKS,
280
- IMPLEMENTATION_CONCEPT_LINKS_NEW,
281
- IMPLEMENTATION_STRUCTURES,
282
- IMPLEMENTATION_STRUCTURES_NEW,
291
+ RULE_IMPLEMENTATIONS,
283
292
  RULE_IMPLEMENTATION_NEW,
284
293
  RULE_IMPLEMENTATION_NEW_RAW,
285
- RULE_IMPLEMENTATIONS,
286
294
  RULE_NEW,
287
295
  SAMPLE,
288
296
  SEARCH,
@@ -300,6 +308,7 @@ const routes = {
300
308
  STRUCTURE,
301
309
  STRUCTURES,
302
310
  STRUCTURES_BULK_UPDATE,
311
+ STRUCTURES_UPLOAD_EVENTS,
303
312
  STRUCTURE_CHILDREN,
304
313
  STRUCTURE_EVENTS,
305
314
  STRUCTURE_GRANTS,
@@ -321,12 +330,6 @@ const routes = {
321
330
  STRUCTURE_VERSION,
322
331
  STRUCTURE_VERSIONS,
323
332
  STRUCTURE_VERSION_VERSIONS,
324
- STRUCTURE,
325
- STRUCTURES_BULK_UPDATE,
326
- STRUCTURES,
327
- STRUCTURES_UPLOAD_EVENTS,
328
- SUBSCRIPTION_EDIT,
329
- SUBSCRIPTION_NEW,
330
333
  SUBSCRIPTION,
331
334
  SUBSCRIPTIONS,
332
335
  SUBSCRIPTION_EDIT,
@@ -0,0 +1,9 @@
1
+ export const toBase64 = (file) =>
2
+ new Promise((resolve, reject) => {
3
+ const reader = new FileReader();
4
+ reader.readAsDataURL(file);
5
+ // eslint-disable-next-line fp/no-mutation
6
+ reader.onload = () => resolve(reader.result);
7
+ // eslint-disable-next-line fp/no-mutation
8
+ reader.onerror = (error) => reject(error);
9
+ });
@@ -2,6 +2,8 @@ import _ from "lodash/fp";
2
2
 
3
3
  export const lowerDeburr = _.flow(_.toLower, _.deburr);
4
4
 
5
+ export const lowerDeburrTrim = _.flow(_.toLower, _.deburr, _.trim);
6
+
5
7
  export const lowerDeburrPath = (path) =>
6
8
  _.flow(_.path(path), _.toLower, _.deburr);
7
9