@truedat/core 5.3.2 → 5.4.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.
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-3806] Hierarchy Selector
8
+
3
9
  ## [5.3.2] 2023-03-13
4
10
 
5
11
  ### Fixed
@@ -12,6 +18,8 @@
12
18
 
13
19
  - [TD-5509] Structure to structure link form Routes
14
20
 
21
+ ## [5.2.9] 2023-02-28
22
+
15
23
  ### Added
16
24
 
17
25
  - [TD-5599] Submenu and Routes for Tasks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "5.3.2",
3
+ "version": "5.4.0",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -35,7 +35,7 @@
35
35
  "@testing-library/jest-dom": "^5.16.5",
36
36
  "@testing-library/react": "^12.0.0",
37
37
  "@testing-library/user-event": "^13.2.1",
38
- "@truedat/test": "5.3.2",
38
+ "@truedat/test": "5.3.3",
39
39
  "babel-jest": "^28.1.0",
40
40
  "babel-plugin-dynamic-import-node": "^2.3.3",
41
41
  "babel-plugin-lodash": "^3.3.4",
@@ -117,5 +117,5 @@
117
117
  "react-dom": ">= 16.8.6 < 17",
118
118
  "semantic-ui-react": ">= 2.0.3 < 2.2"
119
119
  },
120
- "gitHead": "ea33c4f9396f3843573b2508a93473bb3d98ee65"
120
+ "gitHead": "e8b1b4b4323f098ac213ecbd53fe6a005254a672"
121
121
  }
@@ -0,0 +1,52 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useIntl } from "react-intl";
5
+ import { useHierarchy } from "@truedat/df/hooks/useHierarchies";
6
+ import { accentInsensitivePathOrder } from "../services/sort";
7
+ import { stratify, flatten } from "../services/tree";
8
+ import TreeSelector from "./TreeSelector";
9
+
10
+ const HierarchySelector = ({
11
+ multiple,
12
+ hierarchy: hierarchyId,
13
+ onLoad,
14
+ value,
15
+ ...props
16
+ }) => {
17
+ const { formatMessage } = useIntl();
18
+
19
+ const { data: data, error, loading } = useHierarchy(hierarchyId);
20
+ if (error) return null;
21
+ if (loading) return null;
22
+ onLoad(loading);
23
+
24
+ const options = _.flow(
25
+ _.propOr([], "nodes"),
26
+ _.sortBy(accentInsensitivePathOrder("name")),
27
+ stratify({}),
28
+ flatten
29
+ )(data);
30
+
31
+ return (
32
+ <TreeSelector
33
+ options={options}
34
+ placeholder={formatMessage({
35
+ id: multiple
36
+ ? "hierarchy.multiple.placeholder"
37
+ : "hierarchy.selector.placeholder",
38
+ })}
39
+ value={value}
40
+ multiple={multiple}
41
+ {...props}
42
+ />
43
+ );
44
+ };
45
+
46
+ HierarchySelector.propTypes = {
47
+ multiple: PropTypes.bool,
48
+ onLoad: PropTypes.func,
49
+ value: PropTypes.oneOfType([PropTypes.array, PropTypes.number]),
50
+ };
51
+
52
+ export default HierarchySelector;
@@ -12,6 +12,11 @@ export const match =
12
12
  _.contains(query)(lowerDeburr(name));
13
13
  export const matchAny = recursiveMatch(match);
14
14
 
15
+ const maybeMapKeys = _.map((option) => ({
16
+ ...option,
17
+ id: _.has("key")(option) ? option.key : option.id,
18
+ }));
19
+
15
20
  export const SelectedLabels = ({
16
21
  disabled,
17
22
  onClick,
@@ -51,6 +56,7 @@ const toggle = (id) => (value) =>
51
56
  _.includes(id)(value) ? _.without([id])(value) : _.union([id])(value);
52
57
 
53
58
  const labelValues = _.cond([
59
+ [_.isNumber, _.castArray],
54
60
  [_.isArray, _.identity],
55
61
  [_.isEmpty, _.constant([])],
56
62
  [_.isString, _.castArray],
@@ -85,7 +91,7 @@ export const TreeSelector = ({
85
91
  }, [options, open, value]);
86
92
 
87
93
  const handleOpen = (id) => {
88
- const option = _.find({ id })(options);
94
+ const option = _.find({ id })(maybeMapKeys(options));
89
95
  const isOpen = _.contains(id)(open);
90
96
  const childIds = _.map("id")(option.children);
91
97
  const descendentIds = descendents(option);
@@ -129,7 +135,7 @@ export const TreeSelector = ({
129
135
  <SelectedLabels
130
136
  disabled={disabled}
131
137
  placeholder={placeholder}
132
- options={options}
138
+ options={maybeMapKeys(options)}
133
139
  onClick={handleClick}
134
140
  value={labelValues(value)}
135
141
  />
@@ -140,6 +146,7 @@ export const TreeSelector = ({
140
146
  const items = _.flow(
141
147
  filterSearch,
142
148
  filterDisplayed,
149
+ maybeMapKeys,
143
150
  _.map((option) => (
144
151
  <DropdownMenuItem
145
152
  key={option?.id}
@@ -156,6 +163,7 @@ export const TreeSelector = ({
156
163
 
157
164
  return (
158
165
  <Form.Dropdown
166
+ className="fix-dropdown-selector"
159
167
  error={error}
160
168
  floating
161
169
  label={label}
@@ -0,0 +1,91 @@
1
+ import React from "react";
2
+ import { waitFor } from "@testing-library/react";
3
+ import userEvent from "@testing-library/user-event";
4
+ import { render } from "@truedat/test/render";
5
+ import HierarchySelector from "../HierarchySelector";
6
+ import en from "../../messages/en";
7
+
8
+ const hierarchy = {
9
+ id: 123,
10
+ nodes: [
11
+ {
12
+ id: 1,
13
+ parentId: null,
14
+ name: "foo",
15
+ },
16
+ {
17
+ id: 2,
18
+ parentId: 1,
19
+ name: "bar",
20
+ },
21
+ ],
22
+ };
23
+
24
+ jest.mock("react-router-dom", () => ({
25
+ ...jest.requireActual("react-router-dom"),
26
+ useParams: () => ({ id: 123 }),
27
+ }));
28
+
29
+ jest.mock("@truedat/df/hooks/useHierarchies", () => {
30
+ const originalModule = jest.requireActual("@truedat/df/hooks/useHierarchies");
31
+
32
+ return {
33
+ __esModule: true,
34
+ ...originalModule,
35
+ useHierarchy: jest.fn(() => ({
36
+ data: hierarchy,
37
+ loading: false,
38
+ })),
39
+ };
40
+ });
41
+
42
+ const renderOpts = {
43
+ messages: {
44
+ en: {
45
+ ...en,
46
+ },
47
+ },
48
+ fallback: "lazy",
49
+ };
50
+ const props = {
51
+ hierarchy,
52
+ loading: false,
53
+ multiple: true,
54
+ onChange: jest.fn(),
55
+ onLoad: jest.fn(),
56
+ hierarchy: 123,
57
+ };
58
+
59
+ describe("<HierarchySelector />", () => {
60
+ it("matches latest snapshot", async () => {
61
+ const { container, queryByText } = render(
62
+ <HierarchySelector {...props} />,
63
+ renderOpts
64
+ );
65
+ await waitFor(() => {
66
+ expect(queryByText(/foo/)).toBeInTheDocument();
67
+ });
68
+ expect(container).toMatchSnapshot();
69
+ });
70
+
71
+ it("calls onChange with selected values", async () => {
72
+ const { getByText, getByRole } = render(
73
+ <HierarchySelector {...props} />,
74
+ renderOpts
75
+ );
76
+
77
+ await waitFor(() => {
78
+ expect(getByText(/Select Hierarchies/)).toBeTruthy();
79
+ });
80
+
81
+ userEvent.click(getByText(/Select Hierarchies/));
82
+
83
+ await waitFor(() => {
84
+ expect(getByRole("option", { name: /foo/i })).toBeTruthy();
85
+ });
86
+
87
+ userEvent.click(getByRole("option", { name: /foo/i }));
88
+ expect(props.onChange.mock.calls.length).toBe(1);
89
+ expect(props.onChange.mock.calls[0][1]).toEqual({ value: [1] });
90
+ });
91
+ });
@@ -97,7 +97,7 @@ exports[`<CatalogMenu /> matches the latest snapshot 1`] = `
97
97
  <a
98
98
  aria-checked="false"
99
99
  class="item"
100
- href="/bulk_update_template_content_events"
100
+ href="/bulkUpdateTemplateContentEvents"
101
101
  name="structures_upload_events"
102
102
  role="option"
103
103
  >
@@ -3,7 +3,7 @@
3
3
  exports[`<DomainSelector /> matches latest snapshot 1`] = `
4
4
  <div>
5
5
  <div
6
- class="field"
6
+ class="field fix-dropdown-selector"
7
7
  >
8
8
  <div
9
9
  aria-expanded="false"
@@ -0,0 +1,59 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<HierarchySelector /> matches latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="field fix-dropdown-selector"
7
+ >
8
+ <div
9
+ aria-expanded="false"
10
+ aria-multiselectable="true"
11
+ class="ui floating multiple dropdown"
12
+ role="listbox"
13
+ tabindex="0"
14
+ >
15
+ <label>
16
+ Select Hierarchies
17
+ </label>
18
+ <i
19
+ aria-hidden="true"
20
+ class="dropdown icon"
21
+ />
22
+ <div
23
+ class="menu transition"
24
+ >
25
+ <div
26
+ class="ui left icon input search"
27
+ >
28
+ <input
29
+ type="text"
30
+ />
31
+ <i
32
+ aria-hidden="true"
33
+ class="search icon"
34
+ />
35
+ </div>
36
+ <div
37
+ class="scrolling menu transition"
38
+ >
39
+ <div
40
+ aria-selected="false"
41
+ class="item"
42
+ role="option"
43
+ >
44
+ <div
45
+ style="margin-left: 0px;"
46
+ >
47
+ <i
48
+ aria-hidden="true"
49
+ class="plus icon"
50
+ />
51
+ foo
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ `;
@@ -3,7 +3,7 @@
3
3
  exports[`<TreeSelector /> matches latest snapshot 1`] = `
4
4
  <div>
5
5
  <div
6
- class="field"
6
+ class="field fix-dropdown-selector"
7
7
  >
8
8
  <div
9
9
  aria-expanded="false"
@@ -23,6 +23,7 @@ import FiltersLoader from "./FiltersLoader";
23
23
  import GlossaryMenu from "./GlossaryMenu";
24
24
  import GrantMenu from "./GrantMenu";
25
25
  import GroupActions from "./GroupActions";
26
+ import HierarchySelector from "./HierarchySelector";
26
27
  import HistoryBackButton from "./HistoryBackButton";
27
28
  import IngestMenu from "./IngestMenu";
28
29
  import LineageMenu from "./LineageMenu";
@@ -75,6 +76,7 @@ export {
75
76
  GlossaryMenu,
76
77
  GrantMenu,
77
78
  GroupActions,
79
+ HierarchySelector,
78
80
  HistoryBackButton,
79
81
  IngestMenu,
80
82
  LineageMenu,
@@ -56,7 +56,8 @@ export default {
56
56
  "form.validation.required": "{prop} is required",
57
57
  "form.validation.minLength": "{prop} must have at least {value} characters",
58
58
  "form.validation.email.invalid": "Invalid email address",
59
-
59
+ "hierarchy.multiple.placeholder": "Select Hierarchies",
60
+ "hierarchy.selector.placeholder": "Select Hierarchy",
60
61
  "i18n.actions.createMessage": "Create message",
61
62
 
62
63
  "i18n.message.form.messageId.placeholder": "Message ID",
@@ -56,7 +56,8 @@ export default {
56
56
  "form.validation.required": "{prop} es un campo requerido",
57
57
  "form.validation.minLength": "{prop} debe tener al menos {value} elementos",
58
58
  "form.validation.email.invalid": "Dirección de email inválida",
59
-
59
+ "hierarchy.multiple.placeholder": "Seleccionar Jerarquías",
60
+ "hierarchy.selector.placeholder": "Seleccionar Jerarquía",
60
61
  "i18n.actions.createMessage": "Crear mensaje",
61
62
 
62
63
  "i18n.message.form.messageId.placeholder": "Id del mensaje",
package/src/routes.js CHANGED
@@ -174,8 +174,8 @@ export const SOURCE_JOBS = "/sources/:sourceId/jobs";
174
174
  export const SOURCE_JOBS_NEW = "/sources/:sourceId/jobs/new";
175
175
  export const STRUCTURE = "/structures/:id";
176
176
  export const STRUCTURES = "/structures";
177
- export const STRUCTURES_BULK_UPDATE = "/structures/bulk_update";
178
- export const STRUCTURES_UPLOAD_EVENTS = "/bulk_update_template_content_events";
177
+ export const STRUCTURES_BULK_UPDATE = "/structures/bulkUpdate";
178
+ export const STRUCTURES_UPLOAD_EVENTS = "/bulkUpdateTemplateContentEvents";
179
179
  export const STRUCTURE_CHILDREN = "/structures/:id/children";
180
180
  export const STRUCTURE_EVENTS = "/structures/:id/events";
181
181
  export const STRUCTURE_GRANTS = "/structures/:id/grants";