@truedat/df 5.3.1 → 5.3.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.3.3] 2023-03-13
4
+
5
+ ### Added
6
+
7
+ - [TD-3805] Hierarchy widget functionality
8
+
3
9
  ## [5.0.5] 2023-01-27
4
10
 
5
11
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/df",
3
- "version": "5.3.1",
3
+ "version": "5.3.3",
4
4
  "description": "Truedat Web Data Quality Module",
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": "5.3.1",
37
+ "@truedat/test": "5.3.3",
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",
@@ -87,8 +87,8 @@
87
87
  },
88
88
  "dependencies": {
89
89
  "@apollo/client": "^3.7.1",
90
- "@truedat/auth": "5.3.1",
91
- "@truedat/core": "5.3.1",
90
+ "@truedat/auth": "5.3.3",
91
+ "@truedat/core": "5.3.3",
92
92
  "decode-uri-component": "^0.2.2",
93
93
  "path-to-regexp": "^1.7.0",
94
94
  "prop-types": "^15.8.1",
@@ -109,5 +109,5 @@
109
109
  "react-dom": ">= 16.8.6 < 17",
110
110
  "semantic-ui-react": ">= 2.0.3 < 2.2"
111
111
  },
112
- "gitHead": "6f7ae72a2abda5e41480fa5254c3c37447821a45"
112
+ "gitHead": "019b7a3383879ed824b1b494e78e03ad8cc9556f"
113
113
  }
@@ -4,6 +4,7 @@ import PropTypes from "prop-types";
4
4
  import { FormattedMessage, useIntl } from "react-intl";
5
5
  import { Icon, List, Container } from "semantic-ui-react";
6
6
  import DomainPreview from "./widgets/DomainPreview";
7
+ import HierarchyPreview from "./widgets/HierarchyPreview";
7
8
  import SystemPreview from "./widgets/SystemPreview";
8
9
  import FieldViewerValue from "./FieldViewerValue";
9
10
  import "../styles/fieldGroupDetail.less";
@@ -48,6 +49,8 @@ export const DynamicFieldValue = ({
48
49
  )
49
50
  ) : type == "domain" ? (
50
51
  <DomainPreview domainIds={_.castArray(value)} />
52
+ ) : type == "hierarchy" ? (
53
+ <HierarchyPreview hierarchyValue={_.castArray(value)} />
51
54
  ) : _.isArray(value) ? (
52
55
  type == "table" ? (
53
56
  <FieldViewerValue type={type} value={value} values={values} />
@@ -5,6 +5,7 @@ import { connect } from "react-redux";
5
5
  import { Dimmer, Loader } from "semantic-ui-react";
6
6
  import DropdownDataLoader from "./DropdownDataLoader";
7
7
  import DomainDropdown from "./DomainDropdown";
8
+ import HierarchyDropdown from "./HierarchyDropdown";
8
9
  import StandardDropdown from "./StandardDropdown";
9
10
 
10
11
  export const DropdownField = ({ field, loading, onChange, scope }) => {
@@ -13,6 +14,8 @@ export const DropdownField = ({ field, loading, onChange, scope }) => {
13
14
  <Dimmer.Dimmable dimmed={loading}>
14
15
  {type === "domain" ? (
15
16
  <DomainDropdown field={field} onChange={onChange} scope={scope} />
17
+ ) : type === "hierarchy" ? (
18
+ <HierarchyDropdown field={field} onChange={onChange} />
16
19
  ) : (
17
20
  <>
18
21
  <DropdownDataLoader type={type} />
@@ -0,0 +1,56 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Form } from "semantic-ui-react";
5
+ import { HierarchySelector } from "@truedat/core/components";
6
+
7
+ const pickId = _.cond([
8
+ [_.eq(""), _.constant(null)],
9
+ [_.isString, _.identity],
10
+ [_.conformsTo({ id: _.isString }), _.flow(_.prop("id"))],
11
+ [_.stubTrue, _.constant(null)],
12
+ ]);
13
+
14
+ const idOrIds = _.cond([
15
+ [_.isArray, _.flow(_.map(pickId), _.reject(_.isNil))],
16
+ [_.stubTrue, pickId],
17
+ ]);
18
+
19
+ export const HierarchyDropdown = ({
20
+ field: {
21
+ name,
22
+ cardinality,
23
+ value: currentValue,
24
+ values: { hierarchy },
25
+ },
26
+ onChange,
27
+ }) => {
28
+ const [loading, setLoading] = useState(false);
29
+ const handleLoad = () => setLoading(false);
30
+ const handleChange = (e, { value }) => {
31
+ return onChange(e, { name, value: idOrIds(value) });
32
+ };
33
+
34
+ const multiple = _.includes(cardinality)(["+", "*"]);
35
+
36
+ return (
37
+ <>
38
+ {loading ? <Form.Input loading /> : null}
39
+ <HierarchySelector
40
+ multiple={multiple}
41
+ onChange={handleChange}
42
+ onLoad={handleLoad}
43
+ value={idOrIds(currentValue)}
44
+ hierarchy={hierarchy}
45
+ labels
46
+ />
47
+ </>
48
+ );
49
+ };
50
+
51
+ HierarchyDropdown.propTypes = {
52
+ field: PropTypes.object,
53
+ onChange: PropTypes.func,
54
+ };
55
+
56
+ export default HierarchyDropdown;
@@ -0,0 +1,13 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Label } from "semantic-ui-react";
5
+
6
+ export const HierarchyPreview = ({ hierarchyValue }) =>
7
+ _.map(({ id, name }) => <Label key={id}>{name}</Label>)(hierarchyValue);
8
+
9
+ HierarchyPreview.propTypes = {
10
+ hierarchyValue: PropTypes.arrayOf(PropTypes.object).isRequired,
11
+ };
12
+
13
+ export default HierarchyPreview;
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import HierarchyDropdown from "../HierarchyDropdown";
4
+
5
+ describe("<HierarchyDropdown />", () => {
6
+ const props = {
7
+ field: {
8
+ name: "foo",
9
+ cardinality: "*",
10
+ value: "",
11
+ values: { hierarchy: 50 },
12
+ },
13
+ onChange: jest.fn(),
14
+ };
15
+ it("matches the latest snapshot", async () => {
16
+ const { container } = render(<HierarchyDropdown {...props} />);
17
+ expect(container).toMatchSnapshot();
18
+ });
19
+ });
@@ -0,0 +1,3 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<HierarchyDropdown /> matches the latest snapshot 1`] = `<div />`;
@@ -79,6 +79,7 @@ const toFront = (hierarchy) => {
79
79
  _.map((node) => {
80
80
  return {
81
81
  id: node.node_id,
82
+ key: node.key,
82
83
  parentId: node.parent_id,
83
84
  name: node.name,
84
85
  description: node.description,
@@ -72,6 +72,7 @@ export default {
72
72
  "template.field.type.domain": "Domain",
73
73
  "template.field.type.enriched_text": "Enriched text",
74
74
  "template.field.type.float": "Float",
75
+ "template.field.type.hierarchy": "Hierarchy",
75
76
  "template.field.type.image": "Image",
76
77
  "template.field.type.integer": "Integer",
77
78
  "template.field.type.string": "String",
@@ -96,6 +97,7 @@ export default {
96
97
  "template.field.values.file_types": "File types",
97
98
  "template.field.values.fixed_tuple": "Key/Value list",
98
99
  "template.field.values.fixed": "Fixed list",
100
+ "template.field.values.hierarchy": "Select hierarchy",
99
101
  "template.field.values.none": "Free text",
100
102
  "template.field.values.role_groups": "Users or Groups of a Role",
101
103
  "template.field.values.role_users": "Users of a Role",
@@ -72,6 +72,7 @@ export default {
72
72
  "template.field.type.domain": "Dominio",
73
73
  "template.field.type.enriched_text": "Texto enriquecido",
74
74
  "template.field.type.float": "Decimal",
75
+ "template.field.type.hierarchy": "Jerarquía",
75
76
  "template.field.type.image": "Imagen",
76
77
  "template.field.type.integer": "Entero",
77
78
  "template.field.type.string": "Cadena de Texto",
@@ -96,6 +97,7 @@ export default {
96
97
  "template.field.values.file_types": "Tipos de ficheros",
97
98
  "template.field.values.fixed_tuple": "Lista de código/descripción",
98
99
  "template.field.values.fixed": "Lista fija",
100
+ "template.field.values.hierarchy": "Seleccionar jerarquía",
99
101
  "template.field.values.none": "Texto libre",
100
102
  "template.field.values.role_groups": "Usuarios o grupos de un Role",
101
103
  "template.field.values.role_users": "Usuarios de un Role",
@@ -2,7 +2,7 @@ import _ from "lodash/fp";
2
2
  import React, { useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { useIntl } from "react-intl";
5
- import { Form, Button, Divider, Segment, Checkbox } from "semantic-ui-react";
5
+ import { Form, Button, Divider, Segment } from "semantic-ui-react";
6
6
  import ValuesField from "./ValuesField";
7
7
  import ConditionalFieldForm from "./ConditionalFieldForm";
8
8
  import MandatoryConditional from "./MandatoryConditional";
@@ -19,6 +19,8 @@ const typeFromKey = (keys) =>
19
19
  ? "role_groups"
20
20
  : _.includes("role_users")(keys)
21
21
  ? "role_users"
22
+ : _.includes("hierarchy")(keys)
23
+ ? "hierarchy"
22
24
  : _.first(keys);
23
25
 
24
26
  const defaultType = (values) => _.flow(_.map("value"), _.first)(values);
@@ -0,0 +1,66 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { Form } from "semantic-ui-react";
6
+ import { useHierarchies } from "../../../hooks/useHierarchies";
7
+
8
+ export const HierarchiesList = ({
9
+ hierarchies,
10
+ onChange,
11
+ values: value,
12
+ loading,
13
+ }) => {
14
+ const { formatMessage } = useIntl();
15
+
16
+ const handleChange = (_e, { value }) => onChange(value);
17
+
18
+ const options = _.flow(
19
+ _.map(({ id, name }) => ({ text: name, value: id })),
20
+ _.sortBy("text")
21
+ )(hierarchies);
22
+
23
+ return (
24
+ <Form.Group size="small" widths="equal">
25
+ <Form.Dropdown
26
+ required
27
+ selection
28
+ label={
29
+ value === ""
30
+ ? ""
31
+ : formatMessage({ id: "template.field.type.hierarchy" })
32
+ }
33
+ loading={loading}
34
+ placeholder={formatMessage({
35
+ id: "template.field.values.hierarchy",
36
+ })}
37
+ onChange={handleChange}
38
+ options={options}
39
+ value={value}
40
+ />
41
+ </Form.Group>
42
+ );
43
+ };
44
+
45
+ HierarchiesList.propTypes = {
46
+ hierarchies: PropTypes.array,
47
+ onChange: PropTypes.func,
48
+ onDefaultValueChange: PropTypes.func,
49
+ values: PropTypes.number,
50
+ loading: PropTypes.bool,
51
+ };
52
+
53
+ export const HierarchiesListLoader = (props) => {
54
+ const { data: hierarchies, error, loading } = useHierarchies();
55
+
56
+ if (error) return null;
57
+ return (
58
+ <HierarchiesList
59
+ {...props}
60
+ hierarchies={hierarchies || []}
61
+ loading={loading}
62
+ />
63
+ );
64
+ };
65
+
66
+ export default HierarchiesListLoader;
@@ -31,7 +31,7 @@ export const ValuesField = ({
31
31
  ? null
32
32
  : valueSegment(values, keyType, fieldType) && (
33
33
  <Segment>
34
- {_.size(values) > 1 && (
34
+ {_.size(values) > 1 ? (
35
35
  <Form.Dropdown
36
36
  fluid
37
37
  selection
@@ -41,8 +41,8 @@ export const ValuesField = ({
41
41
  required
42
42
  options={valueTypes(values)}
43
43
  />
44
- )}
45
- {keyType && (
44
+ ) : null}
45
+ {keyType ? (
46
46
  <ValuesSelector
47
47
  cardinality={field?.cardinality}
48
48
  defaultField={defaultField}
@@ -53,8 +53,8 @@ export const ValuesField = ({
53
53
  type={keyType}
54
54
  values={_.path(`values.${keyType}`)(field)}
55
55
  />
56
- )}
57
- {_.includes(keyType)(["fixed", "fixed_tuple"]) && (
56
+ ) : null}
57
+ {_.includes(keyType)(["fixed", "fixed_tuple"]) ? (
58
58
  <Form.Checkbox
59
59
  name={subscribableField}
60
60
  label={formatMessage({ id: "template.field.subscribable" })}
@@ -63,7 +63,7 @@ export const ValuesField = ({
63
63
  onChange(null, { name, value })
64
64
  }
65
65
  />
66
- )}
66
+ ) : null}
67
67
  <DefaultValue
68
68
  defaultField={defaultField}
69
69
  field={field}
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
3
3
  import { Form } from "semantic-ui-react";
4
4
  import DependentDomain from "./DependentDomain";
5
5
  import SwitchListForm from "./SwitchListForm";
6
+ import HierarchiesList from "./HierarchiesList";
6
7
  import ValuesListForm from "./ValuesListForm";
7
8
  import { listFormat } from "./valueDefinitions";
8
9
 
@@ -26,14 +27,14 @@ export const ValuesSelector = ({
26
27
  };
27
28
  return (
28
29
  <>
29
- {listFormat(type) && (
30
+ {listFormat(type) ? (
30
31
  <ValuesListForm
31
32
  type={type}
32
33
  values={values || []}
33
34
  onChange={onValueChange}
34
35
  />
35
- )}
36
- {type === "switch" && (
36
+ ) : null}
37
+ {type === "switch" ? (
37
38
  <SwitchListForm
38
39
  cardinality={cardinality}
39
40
  defaultValue={defaultValue}
@@ -42,8 +43,8 @@ export const ValuesSelector = ({
42
43
  onDefaultValueChange={onDefaultValueChange}
43
44
  onChange={onValueChange}
44
45
  />
45
- )}
46
- {type === "domain" && (
46
+ ) : null}
47
+ {type === "domain" ? (
47
48
  <DependentDomain
48
49
  cardinality={cardinality}
49
50
  defaultValue={defaultValue}
@@ -51,8 +52,8 @@ export const ValuesSelector = ({
51
52
  onDefaultValueChange={onDefaultValueChange}
52
53
  values={values || {}}
53
54
  />
54
- )}
55
- {type === "role_users" && (
55
+ ) : null}
56
+ {type === "role_users" ? (
56
57
  <Form.Input
57
58
  value={values || ""}
58
59
  placeholder={formatMessage({
@@ -60,8 +61,8 @@ export const ValuesSelector = ({
60
61
  })}
61
62
  onChange={(e, { value }) => onValueChange(value)}
62
63
  />
63
- )}
64
- {type === "role_groups" && (
64
+ ) : null}
65
+ {type === "role_groups" ? (
65
66
  <Form.Input
66
67
  value={values || ""}
67
68
  placeholder={formatMessage({
@@ -69,7 +70,14 @@ export const ValuesSelector = ({
69
70
  })}
70
71
  onChange={(e, { value }) => onValueChange(value)}
71
72
  />
72
- )}
73
+ ) : null}
74
+ {type === "hierarchy" ? (
75
+ <HierarchiesList
76
+ onChange={onValueChange}
77
+ onDefaultValueChange={onDefaultValueChange}
78
+ values={values || {}}
79
+ />
80
+ ) : null}
73
81
  </>
74
82
  );
75
83
  };
@@ -1230,6 +1230,11 @@ exports[`<FieldForm /> renders ValuesField and manages onChange 1`] = `
1230
1230
  "text": "template.field.type.user_group",
1231
1231
  "value": "user_group",
1232
1232
  },
1233
+ Object {
1234
+ "key": "hierarchy",
1235
+ "text": "template.field.type.hierarchy",
1236
+ "value": "hierarchy",
1237
+ },
1233
1238
  ]
1234
1239
  }
1235
1240
  required={true}
@@ -10,12 +10,14 @@ const valueTypes = [
10
10
  { id: "template.field.values.table_columns", value: "table_columns" },
11
11
  { id: "template.field.values.file_types", value: "file_types" },
12
12
  { id: "template.field.values.domain", value: "domain" },
13
+ { id: "template.field.values.hierarchy", value: "hierarchy" },
13
14
  ];
14
15
 
15
16
  const selectableValues = {
16
17
  string: ["domain", "fixed", "fixed_tuple", "switch"],
17
18
  user: ["role_users"],
18
19
  user_group: ["role_groups"],
20
+ hierarchy: ["hierarchy"],
19
21
  };
20
22
 
21
23
  const eligibleValues = {
@@ -48,7 +50,9 @@ export const defaultValue = (type, fieldType) =>
48
50
  export const valuesSelector = (type) =>
49
51
  type &&
50
52
  (listFormat(type) ||
51
- ["domain", "switch", "role_users", "role_groups"].includes(type));
53
+ ["domain", "switch", "role_users", "role_groups", "hierarchy"].includes(
54
+ type
55
+ ));
52
56
 
53
57
  export const valueSegment = (values, type, fieldType) =>
54
58
  _.size(values) > 1 || valuesSelector(type) || defaultValue(type, fieldType);
@@ -61,7 +61,7 @@ export const WIDGETS = [
61
61
  text: "Dropdown",
62
62
  icon: "arrow down",
63
63
  cardinalities: ["?", "1", "*", "+"],
64
- types: ["string", "system", "domain", "user", "user_group"],
64
+ types: ["string", "system", "domain", "user", "user_group", "hierarchy"],
65
65
  },
66
66
  {
67
67
  key: "radio",