@truedat/dd 7.5.11 → 7.5.13

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "7.5.11",
3
+ "version": "7.5.13",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -46,43 +46,43 @@
46
46
  "devDependencies": {
47
47
  "@testing-library/dom": "^10.4.0",
48
48
  "@testing-library/jest-dom": "^6.6.3",
49
- "@testing-library/react": "^16.2.0",
49
+ "@testing-library/react": "^16.3.0",
50
50
  "@testing-library/user-event": "^14.6.1",
51
- "@truedat/test": "7.5.11",
51
+ "@truedat/test": "7.5.13",
52
52
  "identity-obj-proxy": "^3.0.0",
53
53
  "jest": "^29.7.0",
54
54
  "redux-saga-test-plan": "^4.0.6"
55
55
  },
56
56
  "dependencies": {
57
- "@apollo/client": "^3.13.5",
58
- "axios": "^1.8.4",
57
+ "@apollo/client": "^3.13.8",
58
+ "axios": "^1.9.0",
59
59
  "file-saver": "^2.0.5",
60
- "graphql": "^16.10.0",
60
+ "graphql": "^16.11.0",
61
61
  "is-hotkey": "^0.2.0",
62
62
  "is-url": "^1.2.4",
63
63
  "lodash": "^4.17.21",
64
- "match-sorter": "^8.0.0",
64
+ "match-sorter": "^8.0.1",
65
65
  "moment": "^2.30.1",
66
66
  "path-to-regexp": "^8.2.0",
67
67
  "prop-types": "^15.8.1",
68
- "query-string": "^7.1.2",
69
- "react": "^19.0.0",
68
+ "query-string": "^7.1.3",
69
+ "react": "^19.1.0",
70
70
  "react-csv": "^2.2.2",
71
- "react-dom": "^19.0.0",
71
+ "react-dom": "^19.1.0",
72
72
  "react-dropzone": "^14.3.8",
73
- "react-hook-form": "^7.54.2",
74
- "react-intl": "^7.1.10",
73
+ "react-hook-form": "^7.56.4",
74
+ "react-intl": "^7.1.11",
75
75
  "react-moment": "^1.1.3",
76
76
  "react-redux": "^9.2.0",
77
- "react-router": "^7.4.0",
77
+ "react-router": "^7.6.0",
78
78
  "redux": "^5.0.1",
79
79
  "redux-saga": "^1.3.0",
80
80
  "redux-saga-routines": "^3.2.3",
81
81
  "reselect": "^5.1.1",
82
82
  "semantic-ui-calendar-react": "^0.15.3",
83
83
  "semantic-ui-react": "^3.0.0-beta.2",
84
- "svg-pan-zoom": "^3.6.1",
84
+ "svg-pan-zoom": "^3.6.2",
85
85
  "swr": "^2.3.3"
86
86
  },
87
- "gitHead": "cbb92d5206752fe2add4195432b0401da17a0f23"
87
+ "gitHead": "83de799e59c0d13d4d52812070d232d24f0b5f82"
88
88
  }
package/src/api.js CHANGED
@@ -7,6 +7,7 @@ const API_DATA_STRUCTURES_EDITABLE_CSV = "/api/data_structures/editable_csv";
7
7
  const API_DATA_STRUCTURES_XLSX_DOWNLOAD = "/api/data_structures/xlsx/download";
8
8
  const API_DATA_STRUCTURES_XLSX_UPLOAD = "/api/data_structures/xlsx/upload";
9
9
  const API_DATA_STRUCTURES_SEARCH = "/api/data_structures/search";
10
+ const API_DATA_STRUCTURES_SUGGESTIONS = "/api/data_structures/suggestions";
10
11
  const API_DATA_STRUCTURE_FILTERS_SEARCH = "/api/data_structure_filters/search";
11
12
  const API_DATA_STRUCTURE_VERSION = "/api/data_structures/:id/versions/:version";
12
13
  const API_GRANT_FILTERS_SEARCH = "/api/grant_filters/search";
@@ -63,6 +64,7 @@ export {
63
64
  API_DATA_STRUCTURES_XLSX_DOWNLOAD,
64
65
  API_DATA_STRUCTURES_XLSX_UPLOAD,
65
66
  API_DATA_STRUCTURES_SEARCH,
67
+ API_DATA_STRUCTURES_SUGGESTIONS,
66
68
  API_DATA_STRUCTURE_FILTERS_SEARCH,
67
69
  API_DATA_STRUCTURE_VERSION,
68
70
  API_PROFILE_EXECUTION,
@@ -0,0 +1,88 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useEffect } from "react";
3
+ import { Popup } from "semantic-ui-react";
4
+ import { useIntl } from "react-intl";
5
+ import { useParams } from "react-router";
6
+ import { useSelector } from "react-redux";
7
+ import PropTypes from "prop-types";
8
+ import { useDataStructureSuggestions } from "../hooks/useStructures";
9
+ import { defaultColumnsForStructureSelector } from "../selectors";
10
+ import StructuresSearchResults from "./StructuresSearchResults";
11
+
12
+ const SimilarityColumn = (similarity) => {
13
+ const { formatMessage } = useIntl();
14
+ return (
15
+ <Popup
16
+ content={formatMessage({ id: "structure.similarity.cosine.popup" })}
17
+ trigger={<span>{similarity.toFixed(3)}</span>}
18
+ />
19
+ );
20
+ };
21
+
22
+ const similarityColumnDefinition = {
23
+ name: "similarity",
24
+ fieldSelector: _.prop("similarity"),
25
+ fieldDecorator: SimilarityColumn,
26
+ width: 1,
27
+ };
28
+ const suggestionColumns = (state) =>
29
+ _.concat(defaultColumnsForStructureSelector(state))([
30
+ similarityColumnDefinition,
31
+ ]);
32
+
33
+ export const StructureSuggestions = ({
34
+ selectedStructure,
35
+ handleSelectedStructure,
36
+ selectable,
37
+ }) => {
38
+ const { business_concept_id: id, id: version } = useParams();
39
+ const { conceptLinks, columns } = useSelector((state) => ({
40
+ conceptLinks: state.conceptLinks,
41
+ columns: suggestionColumns(state),
42
+ }));
43
+
44
+ const {
45
+ trigger: fetchSuggestions,
46
+ data,
47
+ isMutating,
48
+ } = useDataStructureSuggestions();
49
+
50
+ useEffect(() => {
51
+ if (id && version && conceptLinks) {
52
+ fetchSuggestions({
53
+ resource: {
54
+ id,
55
+ version,
56
+ type: "concepts",
57
+ links: _.filter(
58
+ ({ resource_type }) => resource_type == "data_structure"
59
+ )(conceptLinks),
60
+ },
61
+ });
62
+ }
63
+ }, [id, version, conceptLinks]);
64
+
65
+ const structures = data?.data?.data || [];
66
+
67
+ return (
68
+ <StructuresSearchResults
69
+ loading={isMutating}
70
+ columns={columns}
71
+ size="small"
72
+ structures={structures}
73
+ selectedStructure={selectedStructure}
74
+ onSelect={handleSelectedStructure}
75
+ labelResults={false}
76
+ pagination={false}
77
+ selectable={selectable}
78
+ />
79
+ );
80
+ };
81
+
82
+ StructureSuggestions.propTypes = {
83
+ handleSelectedStructure: PropTypes.func,
84
+ selectedStructure: PropTypes.object,
85
+ selectable: PropTypes.bool,
86
+ };
87
+
88
+ export default StructureSuggestions;
@@ -18,6 +18,8 @@ export const StructuresSearchResults = ({
18
18
  links,
19
19
  placeToTop,
20
20
  selectable,
21
+ labelResults = true,
22
+ pagination = true,
21
23
  }) => (
22
24
  <Dimmer.Dimmable dimmed={loading} className="structure-table-overflow">
23
25
  {loading ? (
@@ -27,7 +29,7 @@ export const StructuresSearchResults = ({
27
29
  ) : null}
28
30
  {!_.isEmpty(structures) || !_.isEmpty(selectedStructure) ? (
29
31
  <>
30
- <StructuresLabelResults />
32
+ {labelResults && <StructuresLabelResults />}
31
33
  <StructuresTable
32
34
  columns={columns}
33
35
  grantable={grantable}
@@ -39,7 +41,7 @@ export const StructuresSearchResults = ({
39
41
  placeToTop={placeToTop}
40
42
  selectable={selectable}
41
43
  />
42
- <StructuresPagination size={size} />
44
+ {pagination && <StructuresPagination size={size} />}
43
45
  </>
44
46
  ) : (
45
47
  <Message icon info>
@@ -64,8 +66,10 @@ StructuresSearchResults.propTypes = {
64
66
  onSelect: PropTypes.func,
65
67
  structures: PropTypes.array,
66
68
  selectedStructure: PropTypes.object,
69
+ labelResults: PropTypes.bool,
67
70
  loading: PropTypes.bool,
68
71
  links: PropTypes.array,
72
+ pagination: PropTypes.bool,
69
73
  placeToTop: PropTypes.bool,
70
74
  selectable: PropTypes.bool,
71
75
  };
@@ -25,20 +25,21 @@ export const StructuresTable = ({
25
25
  links = [],
26
26
  placeToTop = true,
27
27
  selectable = false,
28
+ structures: overwriteStructures,
28
29
  }) => {
29
- const {
30
- searchData,
31
- sortColumn,
32
- sortDirection,
33
- defaultFilters,
34
- handleSortSelection,
35
- } = useSearchContext();
30
+ const searchContext = useSearchContext();
31
+
32
+ const searchData = searchContext?.searchData;
33
+ const sortColumn = searchContext?.sortColumn;
34
+ const sortDirection = searchContext?.sortDirection;
35
+ const defaultFilters = searchContext?.defaultFilters;
36
+ const handleSortSelection = searchContext?.handleSortSelection;
36
37
 
37
38
  const columns = useSelector((state) =>
38
39
  structureColumnsSelector(state, defaultFilters, overwriteColumns)
39
40
  );
40
41
 
41
- const structures = searchData?.data || [];
42
+ const structures = overwriteStructures || searchData?.data || [];
42
43
 
43
44
  if (_.isEmpty(columns)) {
44
45
  return null;
@@ -103,6 +104,7 @@ StructuresTable.propTypes = {
103
104
  size: PropTypes.string,
104
105
  links: PropTypes.array,
105
106
  placeToTop: PropTypes.bool,
107
+ structures: PropTypes.arrayOf(PropTypes.object),
106
108
  };
107
109
 
108
110
  export default StructuresTable;
@@ -0,0 +1,93 @@
1
+ import React from "react";
2
+ import { render, screen } from "@testing-library/react";
3
+ import { useSelector } from "react-redux";
4
+ import { useParams } from "react-router";
5
+ import { useDataStructureSuggestions } from "../../hooks/useStructures";
6
+ import StructureSuggestions from "../StructureSuggestions";
7
+ import StructuresSearchResults from "../StructuresSearchResults";
8
+ import * as selectors from "../../selectors";
9
+
10
+ jest.mock("react-router", () => ({
11
+ useParams: jest.fn(),
12
+ }));
13
+
14
+ jest.mock("react-redux", () => ({
15
+ useSelector: jest.fn(),
16
+ }));
17
+
18
+ jest.mock("../../hooks/useStructures", () => ({
19
+ useDataStructureSuggestions: jest.fn(),
20
+ }));
21
+
22
+ jest.mock("../StructuresSearchResults", () => jest.fn(() => <div>MockSearchResults</div>));
23
+
24
+ jest.mock("../../selectors", () => ({
25
+ defaultColumnsForStructureSelector: jest.fn(),
26
+ }));
27
+
28
+ describe("StructureSuggestions", () => {
29
+ const mockFetchSuggestions = jest.fn();
30
+
31
+ beforeEach(() => {
32
+ jest.clearAllMocks();
33
+
34
+ selectors.defaultColumnsForStructureSelector.mockReturnValue([
35
+ { name: "name", fieldSelector: (x) => x.name },
36
+ ]);
37
+
38
+ useParams.mockReturnValue({ business_concept_id: "123", id: "456" });
39
+
40
+ useSelector.mockImplementation((selectorFn) =>
41
+ selectorFn({
42
+ conceptLinks: [
43
+ { resource_type: "data_structure", id: "ds1" },
44
+ { resource_type: "other", id: "not-ds" },
45
+ ],
46
+ // mock of defaultColumnsForStructureSelector + similarity column
47
+ columns: [
48
+ { name: "name", fieldSelector: (x) => x.name },
49
+ { name: "similarity", fieldSelector: (x) => x.similarity },
50
+ ],
51
+ })
52
+ );
53
+
54
+ useDataStructureSuggestions.mockReturnValue({
55
+ trigger: mockFetchSuggestions,
56
+ data: {
57
+ data: {
58
+ data: [
59
+ { id: "structure1", similarity: 0.89 },
60
+ { id: "structure2", similarity: 0.77 },
61
+ ],
62
+ },
63
+ },
64
+ isMutating: false,
65
+ });
66
+ });
67
+
68
+ it("calls fetchSuggestions on mount with correct parameters", () => {
69
+ render(<StructureSuggestions selectedStructure={null} handleSelectedStructure={jest.fn()} selectable={true} />);
70
+
71
+ expect(mockFetchSuggestions).toHaveBeenCalledWith({
72
+ resource: {
73
+ id: "123",
74
+ version: "456",
75
+ type: "concepts",
76
+ links: [{ resource_type: "data_structure", id: "ds1" }],
77
+ },
78
+ });
79
+ });
80
+
81
+ it("renders StructuresSearchResults with props", () => {
82
+ render(<StructureSuggestions selectedStructure={{}} handleSelectedStructure={jest.fn()} selectable={false} />);
83
+
84
+ expect(screen.getByText("MockSearchResults")).toBeInTheDocument();
85
+ const props = StructuresSearchResults.mock.calls[0][0];
86
+ expect(props.structures).toEqual([
87
+ { id: "structure1", similarity: 0.89 },
88
+ { id: "structure2", similarity: 0.77 },
89
+ ]);
90
+ expect(props.loading).toBe(false);
91
+ expect(props.selectable).toBe(false);
92
+ });
93
+ });
@@ -12,6 +12,7 @@ import {
12
12
  import {
13
13
  API_SYSTEM_STRUCTURES,
14
14
  API_DATA_STRUCTURES_SEARCH,
15
+ API_DATA_STRUCTURES_SUGGESTIONS,
15
16
  API_DATA_STRUCTURE_FILTERS_SEARCH,
16
17
  API_DATA_STRUCTURES_XLSX_DOWNLOAD,
17
18
  API_DATA_STRUCTURES_XLSX_UPLOAD,
@@ -88,3 +89,9 @@ export const useDataStructureUpload = () => {
88
89
  return apiJsonPost(url, arg, UPLOAD_JSON_OPTS);
89
90
  });
90
91
  };
92
+
93
+ export const useDataStructureSuggestions = () => {
94
+ return useSWRMutations(API_DATA_STRUCTURES_SUGGESTIONS, (url, { arg }) => {
95
+ return apiJsonPost(url, arg);
96
+ });
97
+ };