@truedat/dd 6.0.1 → 6.0.2
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 +6 -6
- package/src/api.js +2 -0
- package/src/components/BucketView.js +4 -3
- package/src/components/CatalogCustomViewCards.js +4 -1
- package/src/components/FilteredNav.js +92 -37
- package/src/components/StructureCrumbs.js +9 -9
- package/src/components/StructureItem.js +43 -6
- package/src/components/StructureItemRoot.js +2 -3
- package/src/components/StructureItems.js +6 -2
- package/src/components/StructureNav.js +10 -1
- package/src/components/StructureNavClassified.js +8 -1
- package/src/components/StructureSearch.js +16 -7
- package/src/components/StructureView.js +1 -1
- package/src/components/StructuresView.js +5 -5
- package/src/components/SystemStructures.js +5 -2
- package/src/components/__tests__/BucketView.spec.js +2 -1
- package/src/components/__tests__/FilteredNav.spec.js +69 -49
- package/src/components/__tests__/StructureItems.spec.js +2 -2
- package/src/components/__tests__/StructureNav.spec.js +1 -1
- package/src/components/__tests__/StructureNavClassified.spec.js +5 -1
- package/src/components/__tests__/StructureStructureLinks.spec.js +8 -7
- package/src/components/__tests__/StructureView.spec.js +1 -0
- package/src/components/__tests__/SystemFilteredNav.spec.js +1 -1
- package/src/hooks/useBucketPaths.js +11 -0
- package/src/hooks/useBucketStructures.js +12 -5
- package/src/reducers/navFilter.js +4 -1
- package/src/reducers/structure.js +1 -0
- package/src/reducers/structureFilters.js +1 -0
- package/src/selectors/getStructureParent.js +1 -2
- package/src/components/__tests__/StructureSearch.spec.js +0 -34
- package/src/components/__tests__/__snapshots__/StructureSearch.spec.js.snap +0 -18
- package/src/utils/bucketNav.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/dd",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"description": "Truedat Web Data Dictionary",
|
|
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": "6.0.
|
|
37
|
+
"@truedat/test": "6.0.2",
|
|
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",
|
|
@@ -88,9 +88,9 @@
|
|
|
88
88
|
},
|
|
89
89
|
"dependencies": {
|
|
90
90
|
"@apollo/client": "^3.7.1",
|
|
91
|
-
"@truedat/auth": "6.0.
|
|
92
|
-
"@truedat/core": "6.0.
|
|
93
|
-
"@truedat/df": "6.0.
|
|
91
|
+
"@truedat/auth": "6.0.2",
|
|
92
|
+
"@truedat/core": "6.0.2",
|
|
93
|
+
"@truedat/df": "6.0.2",
|
|
94
94
|
"lodash": "^4.17.21",
|
|
95
95
|
"moment": "^2.29.4",
|
|
96
96
|
"path-to-regexp": "^1.7.0",
|
|
@@ -115,5 +115,5 @@
|
|
|
115
115
|
"react-dom": ">= 16.8.6 < 17",
|
|
116
116
|
"semantic-ui-react": ">= 2.0.3 < 2.2"
|
|
117
117
|
},
|
|
118
|
-
"gitHead": "
|
|
118
|
+
"gitHead": "dab67cd6f569ca6de2fd8e0b5de260cb5ee261c3"
|
|
119
119
|
}
|
package/src/api.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const API_BUCKET_STRUCTURES = "/api/buckets/structures";
|
|
2
|
+
const API_BUCKET_PATHS = "/api/buckets/paths";
|
|
2
3
|
const API_BUCKET_STRUCTURE = "/api/buckets/structures/:id";
|
|
3
4
|
const API_DATA_STRUCTURE = "/api/data_structures/:id";
|
|
4
5
|
const API_DATA_STRUCTURES_BULK_UPDATE = "/api/data_structures/bulk_update";
|
|
@@ -54,6 +55,7 @@ const API_STRUCTURE_NOTES = "/api/data_structures/:data_structure_id/notes";
|
|
|
54
55
|
|
|
55
56
|
export {
|
|
56
57
|
API_BUCKET_STRUCTURES,
|
|
58
|
+
API_BUCKET_PATHS,
|
|
57
59
|
API_BUCKET_STRUCTURE,
|
|
58
60
|
API_DATA_STRUCTURE,
|
|
59
61
|
API_DATA_STRUCTURES_BULK_UPDATE,
|
|
@@ -7,7 +7,6 @@ import { useParams, useLocation } from "react-router-dom";
|
|
|
7
7
|
import searchImage from "assets/searching.png";
|
|
8
8
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
9
9
|
import { saveNavFilter as saveNavFilterRoutine } from "../routines";
|
|
10
|
-
import { isBucketFilter } from "../utils/bucketNav";
|
|
11
10
|
import FilteredNav from "./FilteredNav";
|
|
12
11
|
import StructureCrumbs from "./StructureCrumbs";
|
|
13
12
|
import StructureGrantCart from "./StructureGrantCart";
|
|
@@ -53,6 +52,7 @@ export const BucketView = ({
|
|
|
53
52
|
getNavFilterURLParams,
|
|
54
53
|
_.values,
|
|
55
54
|
(keyValue) => Object.fromEntries(new Map([keyValue]).entries()),
|
|
55
|
+
(filter) => ({ view: "BUCKET_VIEW", filter }),
|
|
56
56
|
saveNavFilter
|
|
57
57
|
)(location);
|
|
58
58
|
}, [location, saveNavFilter]);
|
|
@@ -68,8 +68,8 @@ export const BucketView = ({
|
|
|
68
68
|
</Grid.Row>
|
|
69
69
|
<Grid.Column width={4}>
|
|
70
70
|
<Segment>
|
|
71
|
-
{
|
|
72
|
-
<FilteredNav
|
|
71
|
+
{navFilter?.view === "BUCKET_VIEW" ? (
|
|
72
|
+
<FilteredNav navFilter={navFilter} />
|
|
73
73
|
) : null}
|
|
74
74
|
</Segment>
|
|
75
75
|
</Grid.Column>
|
|
@@ -90,6 +90,7 @@ export const BucketView = ({
|
|
|
90
90
|
{navFilterOneProp?.propertyPath
|
|
91
91
|
? `${formatMessage({
|
|
92
92
|
id: navFilterOneProp.propertyPath,
|
|
93
|
+
defaultMessage: navFilterOneProp.propertyPath,
|
|
93
94
|
})} > ${navFilterOneProp?.propertyValue}`
|
|
94
95
|
: null}
|
|
95
96
|
</Header>
|
|
@@ -9,6 +9,7 @@ import { FormattedNumber } from "react-intl";
|
|
|
9
9
|
import { CardGroupsAccordion } from "@truedat/core/components";
|
|
10
10
|
import { linkTo } from "@truedat/core/routes";
|
|
11
11
|
import { fetchStructureFilters as fetchStructureFiltersRoutine } from "@truedat/dd/routines";
|
|
12
|
+
import { Loading } from "@truedat/core/components";
|
|
12
13
|
|
|
13
14
|
const moveMissingBucketToTheEnd = (buckets) =>
|
|
14
15
|
_.sortBy(({ key }) => (key === "_missing" || key === "" ? 1 : 0))(buckets);
|
|
@@ -29,7 +30,9 @@ export const CatalogCustomViewCards = ({
|
|
|
29
30
|
const { propertyPath } = useParams();
|
|
30
31
|
const groups = [];
|
|
31
32
|
|
|
32
|
-
return
|
|
33
|
+
return _.isEmpty(structureFilters) ? (
|
|
34
|
+
<Loading />
|
|
35
|
+
) : Object.keys(groups).length > 1 ? (
|
|
33
36
|
<CardGroupsAccordion
|
|
34
37
|
groups={groups}
|
|
35
38
|
cardComponent={CatalogCustomViewCard}
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React, { useState,
|
|
2
|
+
import React, { useState, useRef } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { connect } from "react-redux";
|
|
5
|
-
import {
|
|
6
|
-
import { Button, Icon, Loader } from "semantic-ui-react";
|
|
5
|
+
import { Button, Icon } from "semantic-ui-react";
|
|
7
6
|
import { useIntl } from "react-intl";
|
|
8
|
-
|
|
9
|
-
import { toggleStructuresAliasNameMode } from "../routines";
|
|
7
|
+
|
|
10
8
|
import { getStructureParent } from "../selectors";
|
|
9
|
+
import { toggleStructuresAliasNameMode } from "../routines";
|
|
10
|
+
import { useBucketStructures } from "../hooks/useBucketStructures";
|
|
11
|
+
import { useBucketPaths } from "../hooks/useBucketPaths";
|
|
11
12
|
import StructureNav from "./StructureNav";
|
|
12
13
|
import StructureSearch from "./StructureSearch";
|
|
13
14
|
|
|
14
15
|
export const FilteredNav = ({
|
|
15
16
|
childStructures,
|
|
17
|
+
fnHasCatalogViewProp,
|
|
18
|
+
isLoading,
|
|
16
19
|
parentStructure,
|
|
20
|
+
setSearchValue,
|
|
17
21
|
structuresAliasNameMode,
|
|
18
22
|
toggleStructuresAliasNameMode,
|
|
19
23
|
totalStructures,
|
|
@@ -22,15 +26,6 @@ export const FilteredNav = ({
|
|
|
22
26
|
_.has("original_name")(structure)
|
|
23
27
|
)(childStructures);
|
|
24
28
|
const { formatMessage } = useIntl();
|
|
25
|
-
const [searchValue, setSearchValue] = useState("");
|
|
26
|
-
const onChange = (searchValue) => setSearchValue(searchValue);
|
|
27
|
-
|
|
28
|
-
const filteredStructures = (collection) => {
|
|
29
|
-
const normalizedSearchValue = lowerDeburrTrim(searchValue);
|
|
30
|
-
return _.filter((structure) =>
|
|
31
|
-
_.contains(normalizedSearchValue)(lowerDeburrTrim(structure.name))
|
|
32
|
-
)(collection);
|
|
33
|
-
};
|
|
34
29
|
|
|
35
30
|
return (
|
|
36
31
|
<>
|
|
@@ -46,6 +41,7 @@ export const FilteredNav = ({
|
|
|
46
41
|
icon
|
|
47
42
|
data-tooltip={formatMessage({
|
|
48
43
|
id: "structures.props.functional_name",
|
|
44
|
+
defaultMessage: "structures.props.functional_name",
|
|
49
45
|
})}
|
|
50
46
|
onClick={() => toggleStructuresAliasNameMode(true)}
|
|
51
47
|
>
|
|
@@ -64,12 +60,14 @@ export const FilteredNav = ({
|
|
|
64
60
|
</Button>
|
|
65
61
|
</Button.Group>
|
|
66
62
|
</div>
|
|
67
|
-
<StructureSearch onChange={
|
|
63
|
+
<StructureSearch onChange={setSearchValue} isLoading={isLoading} />
|
|
68
64
|
<StructureNav
|
|
69
|
-
childStructures={
|
|
65
|
+
childStructures={childStructures}
|
|
70
66
|
parentStructure={parentStructure}
|
|
71
67
|
totalChildStructures={totalStructures}
|
|
72
68
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
69
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
70
|
+
isLoading={isLoading}
|
|
73
71
|
/>
|
|
74
72
|
</>
|
|
75
73
|
);
|
|
@@ -77,48 +75,105 @@ export const FilteredNav = ({
|
|
|
77
75
|
|
|
78
76
|
FilteredNav.propTypes = {
|
|
79
77
|
childStructures: PropTypes.array,
|
|
78
|
+
isLoading: PropTypes.bool,
|
|
80
79
|
parentStructure: PropTypes.object,
|
|
80
|
+
setSearchValue: PropTypes.func,
|
|
81
81
|
structuresAliasNameMode: PropTypes.bool,
|
|
82
82
|
toggleStructuresAliasNameMode: PropTypes.func,
|
|
83
83
|
totalStructures: PropTypes.number,
|
|
84
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
84
85
|
};
|
|
85
86
|
|
|
87
|
+
// ElasticSearch identifier null_value
|
|
88
|
+
// It is also the root ID.
|
|
89
|
+
const ES_ID_NULL_VALUE = 0;
|
|
90
|
+
|
|
86
91
|
export const FilteredNavLoader = ({
|
|
87
|
-
|
|
92
|
+
navFilter,
|
|
88
93
|
parentStructure,
|
|
89
94
|
structure,
|
|
90
95
|
structuresAliasNameMode,
|
|
91
96
|
toggleStructuresAliasNameMode,
|
|
92
97
|
}) => {
|
|
93
|
-
const {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
const { data: dataBucketPaths, isLoading: isLoadingBucketPaths } =
|
|
99
|
+
useBucketPaths(navFilter);
|
|
100
|
+
|
|
101
|
+
const bucketPaths = dataBucketPaths?.data;
|
|
102
|
+
|
|
103
|
+
const initialTotalRef = useRef();
|
|
104
|
+
|
|
105
|
+
const subTreeChildren = (ancestry, dsid) =>
|
|
106
|
+
ancestry
|
|
107
|
+
? _.flow(
|
|
108
|
+
_.map(({ data_structure_id }) => data_structure_id),
|
|
109
|
+
(ancestryDsIds) => [...ancestryDsIds, dsid],
|
|
110
|
+
(idPath) => _.get(idPath, bucketPaths?.forest),
|
|
111
|
+
_.keys,
|
|
112
|
+
(keys) => keys || []
|
|
113
|
+
)(ancestry)
|
|
114
|
+
: _.keys(bucketPaths?.forest);
|
|
115
|
+
|
|
116
|
+
const children =
|
|
117
|
+
navFilter.view === "SYSTEM_STRUCTURES"
|
|
118
|
+
? structure?.children
|
|
119
|
+
: [
|
|
120
|
+
...subTreeChildren(structure?.ancestry, structure?.id),
|
|
121
|
+
...(bucketPaths?.filtered_children[structure?.id || 0] || []),
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
const parentId =
|
|
125
|
+
structure?.parents?.[0]?.data_structure_id || ES_ID_NULL_VALUE;
|
|
126
|
+
|
|
127
|
+
const childrenOrSiblings = !_.isEmpty(children)
|
|
128
|
+
? children
|
|
129
|
+
: [
|
|
130
|
+
...subTreeChildren(structure?.ancestry.slice(0, -1), structure?.id),
|
|
131
|
+
...(bucketPaths?.filtered_children[parentId] || []),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
// If there are children, show children (next parent_id is structure id)
|
|
135
|
+
// If there are no children, show siblings (next parent_id is structure parent id)
|
|
136
|
+
const parentOrCurrentId = _.isEmpty(children)
|
|
137
|
+
? parentId
|
|
138
|
+
: structure?.id || ES_ID_NULL_VALUE;
|
|
139
|
+
|
|
140
|
+
const initialSearchValue = "";
|
|
141
|
+
const [searchValue, setSearchValue] = useState(initialSearchValue);
|
|
142
|
+
|
|
143
|
+
const { data: dataBucketStructures, isLoading } = useBucketStructures({
|
|
144
|
+
navFilter,
|
|
145
|
+
parentFilter: { parent_id: parentOrCurrentId },
|
|
146
|
+
ids: childrenOrSiblings,
|
|
147
|
+
query: searchValue,
|
|
148
|
+
});
|
|
149
|
+
if (searchValue === initialSearchValue) {
|
|
150
|
+
initialTotalRef.current = dataBucketStructures?.headers["x-total-count"];
|
|
151
|
+
}
|
|
152
|
+
const bucketStructures = dataBucketStructures?.data?.data;
|
|
153
|
+
|
|
154
|
+
const fnHasCatalogViewProp = (structure) =>
|
|
155
|
+
navFilter?.view === "BUCKET_VIEW"
|
|
156
|
+
? bucketPaths?.filtered_children[parentOrCurrentId]?.includes(
|
|
157
|
+
structure?.id
|
|
158
|
+
) || false
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
return (
|
|
110
162
|
<FilteredNav
|
|
111
|
-
childStructures={
|
|
163
|
+
childStructures={bucketStructures || []}
|
|
112
164
|
parentStructure={parentStructure}
|
|
113
|
-
totalStructures={
|
|
165
|
+
totalStructures={initialTotalRef.current}
|
|
166
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
114
167
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
115
168
|
toggleStructuresAliasNameMode={toggleStructuresAliasNameMode}
|
|
169
|
+
setSearchValue={setSearchValue}
|
|
170
|
+
isLoading={isLoading || isLoadingBucketPaths}
|
|
116
171
|
/>
|
|
117
172
|
);
|
|
118
173
|
};
|
|
119
174
|
|
|
120
175
|
FilteredNavLoader.propTypes = {
|
|
121
|
-
|
|
176
|
+
navFilter: PropTypes.object,
|
|
122
177
|
parentStructure: PropTypes.object,
|
|
123
178
|
structure: PropTypes.object,
|
|
124
179
|
structuresAliasNameMode: PropTypes.bool,
|
|
@@ -18,21 +18,21 @@ export const StructuresCrumb = () => (
|
|
|
18
18
|
|
|
19
19
|
const filterQueryParams = _.flow(
|
|
20
20
|
Object.entries,
|
|
21
|
-
_.first,
|
|
21
|
+
_.first, // only works for one filter
|
|
22
22
|
([propertyPath, propertyValue]) => ({
|
|
23
23
|
propertyPath,
|
|
24
24
|
propertyValue,
|
|
25
25
|
}),
|
|
26
|
-
(queryParams) => new URLSearchParams(queryParams)
|
|
26
|
+
(queryParams) => new URLSearchParams(queryParams),
|
|
27
27
|
);
|
|
28
28
|
|
|
29
29
|
export const SystemCrumb = ({ id, name, navFilter }) => {
|
|
30
|
-
const [propertyPath, propertyValue] = !_.isEmpty(navFilter)
|
|
31
|
-
? _.toPairs(navFilter)[0]
|
|
30
|
+
const [propertyPath, propertyValue] = !_.isEmpty(navFilter.filter)
|
|
31
|
+
? _.toPairs(navFilter.filter)[0]
|
|
32
32
|
: [null, null];
|
|
33
33
|
return (
|
|
34
34
|
<>
|
|
35
|
-
{
|
|
35
|
+
{navFilter.view === "SYSTEM_STRUCTURES" ? (
|
|
36
36
|
<Breadcrumb.Section
|
|
37
37
|
as={Link}
|
|
38
38
|
to={linkTo.SYSTEM_STRUCTURES({ id })}
|
|
@@ -40,25 +40,25 @@ export const SystemCrumb = ({ id, name, navFilter }) => {
|
|
|
40
40
|
>
|
|
41
41
|
{name}
|
|
42
42
|
</Breadcrumb.Section>
|
|
43
|
-
) : (
|
|
43
|
+
) : navFilter.view === "BUCKET_VIEW" ? (
|
|
44
44
|
<>
|
|
45
45
|
<Breadcrumb.Section
|
|
46
46
|
as={Link}
|
|
47
47
|
to={`${linkTo.BUCKETS_VIEW({ propertyPath })}`}
|
|
48
48
|
active={false}
|
|
49
49
|
>
|
|
50
|
-
<FormattedMessage id={propertyPath} />
|
|
50
|
+
<FormattedMessage id={propertyPath} defaultMessage={propertyPath} />
|
|
51
51
|
</Breadcrumb.Section>
|
|
52
52
|
<Breadcrumb.Divider icon="right angle" />
|
|
53
53
|
<Breadcrumb.Section
|
|
54
54
|
as={Link}
|
|
55
|
-
to={`${linkTo.BUCKET_VIEW()}?${filterQueryParams(navFilter)}`}
|
|
55
|
+
to={`${linkTo.BUCKET_VIEW()}?${filterQueryParams(navFilter.filter)}`}
|
|
56
56
|
active={false}
|
|
57
57
|
>
|
|
58
58
|
{propertyValue}
|
|
59
59
|
</Breadcrumb.Section>
|
|
60
60
|
</>
|
|
61
|
-
)}
|
|
61
|
+
) : undefined}
|
|
62
62
|
<Breadcrumb.Divider icon="right angle" />
|
|
63
63
|
</>
|
|
64
64
|
);
|
|
@@ -23,9 +23,12 @@ const filterQueryParams = _.flow(
|
|
|
23
23
|
(queryParams) => new URLSearchParams(queryParams)
|
|
24
24
|
);
|
|
25
25
|
|
|
26
|
-
const bucketToBreadcrumbs = ({
|
|
27
|
-
const [propertyPath, propertyValue] = _.toPairs(
|
|
28
|
-
return `${formatMessage({
|
|
26
|
+
const bucketToBreadcrumbs = ({ filter, formatMessage }) => {
|
|
27
|
+
const [propertyPath, propertyValue] = _.toPairs(filter)[0];
|
|
28
|
+
return `${formatMessage({
|
|
29
|
+
id: propertyPath,
|
|
30
|
+
defaultMessage: propertyPath,
|
|
31
|
+
})} > ${propertyValue}`;
|
|
29
32
|
};
|
|
30
33
|
|
|
31
34
|
export const StructureItem = ({
|
|
@@ -37,11 +40,44 @@ export const StructureItem = ({
|
|
|
37
40
|
navFilter,
|
|
38
41
|
original_name,
|
|
39
42
|
type,
|
|
43
|
+
/* hasCatalogViewProp can be:
|
|
44
|
+
* - true/false in bucket view (catalog view)
|
|
45
|
+
* - undefined in regular structure view
|
|
46
|
+
*/
|
|
47
|
+
hasCatalogViewProp,
|
|
40
48
|
}) => {
|
|
41
49
|
const { formatMessage } = useIntl();
|
|
42
50
|
|
|
43
51
|
const displayName = structuresAliasNameMode ? name : original_name || name;
|
|
44
52
|
|
|
53
|
+
const truthyToClassName = {
|
|
54
|
+
deleted_at: "deleted",
|
|
55
|
+
hasCatalogViewProp: "has-catalog-view-prop",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const notUndefinedToClassName = {
|
|
59
|
+
hasCatalogViewProp: "has-no-catalog-view-prop",
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const toClassNames = _.flow(
|
|
63
|
+
Object.entries,
|
|
64
|
+
_.reduce(
|
|
65
|
+
(acc, [key, value]) =>
|
|
66
|
+
value
|
|
67
|
+
? [truthyToClassName[key], ...acc]
|
|
68
|
+
: value !== undefined
|
|
69
|
+
? [
|
|
70
|
+
...(notUndefinedToClassName[key]
|
|
71
|
+
? [notUndefinedToClassName[key]]
|
|
72
|
+
: []),
|
|
73
|
+
...acc,
|
|
74
|
+
]
|
|
75
|
+
: acc,
|
|
76
|
+
[]
|
|
77
|
+
),
|
|
78
|
+
_.join(" ")
|
|
79
|
+
);
|
|
80
|
+
|
|
45
81
|
return (
|
|
46
82
|
<List.Item
|
|
47
83
|
as={active ? "a" : Link}
|
|
@@ -52,19 +88,19 @@ export const StructureItem = ({
|
|
|
52
88
|
: type == "system" && id
|
|
53
89
|
? linkTo.SYSTEM_STRUCTURES({ id })
|
|
54
90
|
: type == "bucket"
|
|
55
|
-
? `${linkTo.BUCKET_VIEW()}?${filterQueryParams(navFilter)}`
|
|
91
|
+
? `${linkTo.BUCKET_VIEW()}?${filterQueryParams(navFilter.filter)}`
|
|
56
92
|
: id
|
|
57
93
|
? linkTo.STRUCTURE({ id })
|
|
58
94
|
: STRUCTURES
|
|
59
95
|
}
|
|
60
|
-
className={deleted_at
|
|
96
|
+
className={toClassNames({ deleted_at, hasCatalogViewProp })}
|
|
61
97
|
active={active}
|
|
62
98
|
>
|
|
63
99
|
<List.Icon name={getIcon(formatMessage, type)} verticalAlign="middle" />
|
|
64
100
|
<List.Content>
|
|
65
101
|
<List.Description>
|
|
66
102
|
{type == "bucket"
|
|
67
|
-
? bucketToBreadcrumbs({ navFilter, formatMessage })
|
|
103
|
+
? bucketToBreadcrumbs({ filter: navFilter.filter, formatMessage })
|
|
68
104
|
: displayName}
|
|
69
105
|
</List.Description>
|
|
70
106
|
</List.Content>
|
|
@@ -81,6 +117,7 @@ StructureItem.propTypes = {
|
|
|
81
117
|
original_name: PropTypes.string,
|
|
82
118
|
structuresAliasNameMode: PropTypes.bool,
|
|
83
119
|
type: PropTypes.string,
|
|
120
|
+
hasCatalogViewProp: PropTypes.bool,
|
|
84
121
|
};
|
|
85
122
|
|
|
86
123
|
export const mapStateToProps = (
|
|
@@ -6,7 +6,6 @@ import { useHistory } from "react-router-dom";
|
|
|
6
6
|
import PropTypes from "prop-types";
|
|
7
7
|
import { List } from "semantic-ui-react";
|
|
8
8
|
import { STRUCTURES, linkTo } from "@truedat/core/routes";
|
|
9
|
-
import { isBucketFilter } from "../utils/bucketNav";
|
|
10
9
|
|
|
11
10
|
const filterParams = _.flow(
|
|
12
11
|
Object.entries,
|
|
@@ -19,9 +18,9 @@ const filterParams = _.flow(
|
|
|
19
18
|
|
|
20
19
|
export const StructureItemRoot = ({ navFilter }) => {
|
|
21
20
|
const history = useHistory();
|
|
22
|
-
const url =
|
|
21
|
+
const url = navFilter?.view === "BUCKET_VIEW"
|
|
23
22
|
? linkTo.BUCKETS_VIEW({
|
|
24
|
-
propertyPath: filterParams(navFilter)?.propertyPath,
|
|
23
|
+
propertyPath: filterParams(navFilter.filter)?.propertyPath,
|
|
25
24
|
})
|
|
26
25
|
: STRUCTURES;
|
|
27
26
|
return (
|
|
@@ -3,13 +3,16 @@ import PropTypes from "prop-types";
|
|
|
3
3
|
import StructureItem from "./StructureItem";
|
|
4
4
|
|
|
5
5
|
export const StructureItems = ({
|
|
6
|
-
bucketChildStructures,
|
|
7
6
|
structures,
|
|
8
7
|
structuresAliasNameMode,
|
|
8
|
+
fnHasCatalogViewProp,
|
|
9
9
|
}) => {
|
|
10
|
-
return
|
|
10
|
+
return structures.map((structure, index) => (
|
|
11
11
|
<StructureItem
|
|
12
12
|
key={index}
|
|
13
|
+
hasCatalogViewProp={
|
|
14
|
+
fnHasCatalogViewProp ? fnHasCatalogViewProp(structure) : undefined
|
|
15
|
+
}
|
|
13
16
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
14
17
|
{...structure}
|
|
15
18
|
/>
|
|
@@ -19,6 +22,7 @@ export const StructureItems = ({
|
|
|
19
22
|
StructureItems.propTypes = {
|
|
20
23
|
structures: PropTypes.array,
|
|
21
24
|
structuresAliasNameMode: PropTypes.bool,
|
|
25
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
export default StructureItems;
|
|
@@ -2,6 +2,7 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import React from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { List, Divider } from "semantic-ui-react";
|
|
5
|
+
import { Loading } from "@truedat/core/components";
|
|
5
6
|
import { useIntl } from "react-intl";
|
|
6
7
|
import StructureItemRoot from "./StructureItemRoot";
|
|
7
8
|
import StructureItem from "./StructureItem";
|
|
@@ -20,6 +21,8 @@ export const StructureNav = ({
|
|
|
20
21
|
parentStructure,
|
|
21
22
|
structuresAliasNameMode,
|
|
22
23
|
totalChildStructures,
|
|
24
|
+
fnHasCatalogViewProp,
|
|
25
|
+
isLoading,
|
|
23
26
|
}) => {
|
|
24
27
|
const { formatMessage } = useIntl();
|
|
25
28
|
const firstWithClasses = _.find(hasNavClass)(childStructures);
|
|
@@ -27,7 +30,9 @@ export const StructureNav = ({
|
|
|
27
30
|
? _.flow(navClasses, _.head)(firstWithClasses)
|
|
28
31
|
: undefined;
|
|
29
32
|
|
|
30
|
-
return (
|
|
33
|
+
return isLoading ? (
|
|
34
|
+
<Loading />
|
|
35
|
+
) : (
|
|
31
36
|
<>
|
|
32
37
|
<List relaxed selection className="structure-nav">
|
|
33
38
|
<StructureItemRoot />
|
|
@@ -42,11 +47,13 @@ export const StructureNav = ({
|
|
|
42
47
|
<StructureNavClassified
|
|
43
48
|
classifier={classifier}
|
|
44
49
|
structures={childStructures}
|
|
50
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
45
51
|
/>
|
|
46
52
|
) : (
|
|
47
53
|
<StructureItems
|
|
48
54
|
structures={childStructures}
|
|
49
55
|
structuresAliasNameMode={structuresAliasNameMode}
|
|
56
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
50
57
|
/>
|
|
51
58
|
)}
|
|
52
59
|
</List>
|
|
@@ -67,9 +74,11 @@ export const StructureNav = ({
|
|
|
67
74
|
|
|
68
75
|
StructureNav.propTypes = {
|
|
69
76
|
childStructures: PropTypes.array,
|
|
77
|
+
isLoading: PropTypes.bool,
|
|
70
78
|
parentStructure: PropTypes.object,
|
|
71
79
|
structuresAliasNameMode: PropTypes.bool,
|
|
72
80
|
totalChildStructures: PropTypes.number,
|
|
81
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
export default StructureNav;
|
|
@@ -11,6 +11,7 @@ export const StructureNavClassified = ({
|
|
|
11
11
|
classifier,
|
|
12
12
|
structures,
|
|
13
13
|
leafClasses,
|
|
14
|
+
fnHasCatalogViewProp,
|
|
14
15
|
}) => {
|
|
15
16
|
const { formatMessage } = useIntl();
|
|
16
17
|
|
|
@@ -43,7 +44,12 @@ export const StructureNavClassified = ({
|
|
|
43
44
|
className: "noselect",
|
|
44
45
|
}}
|
|
45
46
|
/>
|
|
46
|
-
{expanded[label] ?
|
|
47
|
+
{expanded[label] ? (
|
|
48
|
+
<StructureItems
|
|
49
|
+
structures={children}
|
|
50
|
+
fnHasCatalogViewProp={fnHasCatalogViewProp}
|
|
51
|
+
/>
|
|
52
|
+
) : null}
|
|
47
53
|
</React.Fragment>
|
|
48
54
|
));
|
|
49
55
|
};
|
|
@@ -52,6 +58,7 @@ StructureNavClassified.propTypes = {
|
|
|
52
58
|
classifier: PropTypes.string,
|
|
53
59
|
structures: PropTypes.array,
|
|
54
60
|
leafClasses: PropTypes.object,
|
|
61
|
+
fnHasCatalogViewProp: PropTypes.func,
|
|
55
62
|
};
|
|
56
63
|
|
|
57
64
|
export const mapStateToProps = (state) => ({
|
|
@@ -1,20 +1,29 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
1
2
|
import React from "react";
|
|
2
3
|
import PropTypes from "prop-types";
|
|
3
4
|
import { useIntl } from "react-intl";
|
|
4
5
|
import { Input } from "semantic-ui-react";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
7
|
+
// https://stackoverflow.com/a/37312154
|
|
8
|
+
const debounceEventHandler = (...args) => {
|
|
9
|
+
const debounced = _.debounce(...args);
|
|
10
|
+
return (e) => {
|
|
11
|
+
e.persist();
|
|
12
|
+
return debounced(e);
|
|
10
13
|
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const StructureSearch = ({ onChange, isLoading = false }) => {
|
|
17
|
+
const { formatMessage } = useIntl();
|
|
18
|
+
const handleChange = (e) => onChange(e.target.value);
|
|
19
|
+
|
|
11
20
|
return (
|
|
12
21
|
<Input
|
|
13
|
-
onChange={handleChange}
|
|
22
|
+
onChange={debounceEventHandler(500, handleChange)}
|
|
14
23
|
icon={{ name: "search", link: true }}
|
|
15
24
|
iconPosition="left"
|
|
16
25
|
fluid
|
|
17
|
-
loading={
|
|
26
|
+
loading={isLoading}
|
|
18
27
|
placeholder={formatMessage({ id: "structure.search.placeholder" })}
|
|
19
28
|
/>
|
|
20
29
|
);
|
|
@@ -22,7 +31,7 @@ export const StructureSearch = ({ onChange, loading = false }) => {
|
|
|
22
31
|
|
|
23
32
|
StructureSearch.propTypes = {
|
|
24
33
|
onChange: PropTypes.func,
|
|
25
|
-
|
|
34
|
+
isLoading: PropTypes.bool,
|
|
26
35
|
};
|
|
27
36
|
|
|
28
37
|
export default StructureSearch;
|
|
@@ -34,7 +34,7 @@ export const StructureView = ({
|
|
|
34
34
|
<Grid.Row stretched>
|
|
35
35
|
<Grid.Column width={4}>
|
|
36
36
|
<Segment id="filter-nav-container">
|
|
37
|
-
<FilteredNav
|
|
37
|
+
<FilteredNav navFilter={navFilter} structure={structure} />
|
|
38
38
|
</Segment>
|
|
39
39
|
</Grid.Column>
|
|
40
40
|
<Grid.Column width={showGrantRequestCart ? 8 : 12}>
|
|
@@ -19,6 +19,9 @@ import CatalogCustomViewCards from "./CatalogCustomViewCards";
|
|
|
19
19
|
|
|
20
20
|
const StructuresHeader = () => {
|
|
21
21
|
const match = useRouteMatch(BUCKETS_VIEW);
|
|
22
|
+
const propertyPathMessage = !match?.params?.propertyPath
|
|
23
|
+
? "structures.subheader"
|
|
24
|
+
: match?.params?.propertyPath;
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
27
|
<Header as="h2">
|
|
@@ -33,11 +36,8 @@ const StructuresHeader = () => {
|
|
|
33
36
|
/>
|
|
34
37
|
<Header.Subheader>
|
|
35
38
|
<FormattedMessage
|
|
36
|
-
id={
|
|
37
|
-
|
|
38
|
-
? "structures.subheader"
|
|
39
|
-
: match?.params?.propertyPath
|
|
40
|
-
}
|
|
39
|
+
id={propertyPathMessage}
|
|
40
|
+
defaultMessage={propertyPathMessage}
|
|
41
41
|
/>
|
|
42
42
|
</Header.Subheader>
|
|
43
43
|
</Header.Content>
|
|
@@ -21,7 +21,10 @@ export const SystemStructures = ({
|
|
|
21
21
|
const { id: systemId } = useParams();
|
|
22
22
|
|
|
23
23
|
useEffect(() => {
|
|
24
|
-
saveNavFilter({
|
|
24
|
+
saveNavFilter({
|
|
25
|
+
view: "SYSTEM_STRUCTURES",
|
|
26
|
+
filter: { system_id: systemId },
|
|
27
|
+
});
|
|
25
28
|
}, [systemId, saveNavFilter]);
|
|
26
29
|
|
|
27
30
|
return (
|
|
@@ -35,7 +38,7 @@ export const SystemStructures = ({
|
|
|
35
38
|
<Grid.Row stretched>
|
|
36
39
|
<Grid.Column width={4}>
|
|
37
40
|
<Segment>
|
|
38
|
-
{navFilter?.system_id ? <FilteredNav
|
|
41
|
+
{navFilter?.filter?.system_id ? <FilteredNav navFilter={navFilter} /> : null}
|
|
39
42
|
</Segment>
|
|
40
43
|
</Grid.Column>
|
|
41
44
|
<Grid.Column width={12}>
|
|
@@ -1,112 +1,132 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import userEvent from "@testing-library/user-event";
|
|
3
3
|
import { render } from "@truedat/test/render";
|
|
4
|
+
import { act } from "react-dom/test-utils";
|
|
4
5
|
|
|
5
6
|
import FilteredNavLoader, { FilteredNav } from "../FilteredNav";
|
|
6
7
|
|
|
7
8
|
jest.mock("../../hooks/useBucketStructures", () => {
|
|
8
9
|
const originalModule = jest.requireActual("../../hooks/useBucketStructures");
|
|
10
|
+
const _ = require("lodash/fp");
|
|
11
|
+
const { lowerDeburrTrim } = require("@truedat/core/services/sort");
|
|
9
12
|
|
|
10
13
|
const data = [
|
|
11
14
|
{ id: 1, name: "bank_capital" },
|
|
12
15
|
{ id: 2, name: "consumorenovables" },
|
|
13
16
|
];
|
|
14
17
|
|
|
18
|
+
const elasticSearchMock = (collection, searchValue) => {
|
|
19
|
+
const normalizedSearchValue = lowerDeburrTrim(searchValue);
|
|
20
|
+
return _.filter((structure) =>
|
|
21
|
+
_.contains(normalizedSearchValue)(lowerDeburrTrim(structure.name))
|
|
22
|
+
)(collection);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
__esModule: true,
|
|
27
|
+
...originalModule,
|
|
28
|
+
useBucketStructures: jest.fn(({ query }) => {
|
|
29
|
+
const elasticSearchResult = elasticSearchMock(data, query);
|
|
30
|
+
return {
|
|
31
|
+
data: {
|
|
32
|
+
data: { data: elasticSearchResult },
|
|
33
|
+
headers: { "x-total-count": elasticSearchResult.length },
|
|
34
|
+
},
|
|
35
|
+
error: false,
|
|
36
|
+
isLoading: false,
|
|
37
|
+
};
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
jest.mock("../../hooks/useBucketPaths", () => {
|
|
43
|
+
const originalModule = jest.requireActual("../../hooks/useBucketPaths");
|
|
44
|
+
|
|
45
|
+
const data = {
|
|
46
|
+
forest: {},
|
|
47
|
+
filtered_children: {},
|
|
48
|
+
};
|
|
49
|
+
|
|
15
50
|
return {
|
|
16
51
|
__esModule: true,
|
|
17
52
|
...originalModule,
|
|
18
|
-
|
|
19
|
-
data: { data
|
|
53
|
+
useBucketPaths: jest.fn(() => ({
|
|
54
|
+
data: { data, headers: { "x-total-count": 2 } },
|
|
20
55
|
error: false,
|
|
21
|
-
|
|
56
|
+
isLoading: false,
|
|
22
57
|
})),
|
|
23
58
|
};
|
|
24
59
|
});
|
|
25
60
|
|
|
61
|
+
const navFilter = { view: "SYSTEM_STRUCTURES", filter: {} };
|
|
62
|
+
|
|
26
63
|
const renderOpts = {
|
|
27
64
|
state: {
|
|
28
|
-
navFilter
|
|
65
|
+
navFilter,
|
|
29
66
|
},
|
|
30
67
|
};
|
|
31
68
|
|
|
32
69
|
describe("<FilteredNav />", () => {
|
|
33
|
-
it("
|
|
34
|
-
const
|
|
35
|
-
{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const parentStructures = [];
|
|
39
|
-
const props = { childStructures, parentStructures };
|
|
40
|
-
|
|
41
|
-
const { getByText } = render(<FilteredNav {...props} />, renderOpts);
|
|
70
|
+
it("FilterNavLoader loads from useBucketStructures and shows structures", () => {
|
|
71
|
+
const { getByText } = render(
|
|
72
|
+
<FilteredNavLoader navFilter={navFilter} />,
|
|
73
|
+
renderOpts
|
|
74
|
+
);
|
|
42
75
|
expect(getByText("bank_capital")).toBeInTheDocument();
|
|
43
76
|
expect(getByText("consumorenovables")).toBeInTheDocument();
|
|
44
77
|
});
|
|
45
78
|
|
|
46
|
-
it("
|
|
47
|
-
const childStructures = [
|
|
48
|
-
{ id: 1, name: "bank_capital" },
|
|
49
|
-
{ id: 2, name: "consumorenovables" },
|
|
50
|
-
];
|
|
51
|
-
const parentStructures = [];
|
|
52
|
-
const props = { childStructures, parentStructures };
|
|
53
|
-
|
|
79
|
+
it("StructureSearch: one element matching", async () => {
|
|
54
80
|
const { getByRole, getByText, queryByText } = render(
|
|
55
|
-
<
|
|
81
|
+
<FilteredNavLoader navFilter={navFilter} />,
|
|
56
82
|
renderOpts
|
|
57
83
|
);
|
|
58
84
|
expect(getByText("bank_capital")).toBeInTheDocument();
|
|
59
85
|
expect(getByText("consumorenovables")).toBeInTheDocument();
|
|
60
86
|
|
|
87
|
+
jest.useFakeTimers();
|
|
61
88
|
const inputSearch = getByRole("textbox");
|
|
62
89
|
userEvent.type(inputSearch, "bank");
|
|
90
|
+
act(() => jest.runAllTimers());
|
|
63
91
|
expect(getByText("bank_capital")).toBeInTheDocument();
|
|
64
92
|
expect(queryByText("consumorenovables")).not.toBeInTheDocument();
|
|
65
93
|
});
|
|
66
94
|
|
|
67
|
-
it("
|
|
68
|
-
const childStructures = [
|
|
69
|
-
{ id: 1, name: "bank_capital" },
|
|
70
|
-
{ id: 2, name: "consumorenovables" },
|
|
71
|
-
];
|
|
72
|
-
const parentStructures = [];
|
|
73
|
-
const props = { childStructures, parentStructures };
|
|
74
|
-
|
|
95
|
+
it("StructureSearch: no elements matching", () => {
|
|
75
96
|
const { getByRole, getByText, queryByText } = render(
|
|
76
|
-
<
|
|
97
|
+
<FilteredNavLoader navFilter={navFilter} />,
|
|
77
98
|
renderOpts
|
|
78
99
|
);
|
|
79
100
|
expect(getByText("bank_capital")).toBeInTheDocument();
|
|
80
101
|
expect(getByText("consumorenovables")).toBeInTheDocument();
|
|
81
102
|
|
|
103
|
+
jest.useFakeTimers();
|
|
82
104
|
const inputSearch = getByRole("textbox");
|
|
83
105
|
userEvent.type(inputSearch, "renovabless");
|
|
84
|
-
|
|
106
|
+
act(() => jest.runAllTimers());
|
|
85
107
|
expect(queryByText("bank_capital")).not.toBeInTheDocument();
|
|
86
108
|
expect(queryByText("consumorenovables")).not.toBeInTheDocument();
|
|
87
109
|
});
|
|
88
110
|
|
|
89
|
-
it("FilterNavLoader
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
it("FilterNavLoader shows displayed/total structures message", () => {
|
|
96
|
-
const { queryByText, getByText, getByRole } = render(
|
|
97
|
-
<FilteredNavLoader />,
|
|
98
|
-
renderOpts
|
|
99
|
-
);
|
|
111
|
+
// it("FilterNavLoader shows displayed/total structures message", () => {
|
|
112
|
+
// const { queryByText, getByText, getByRole } = render(
|
|
113
|
+
// <FilteredNavLoader navFilter={navFilter} />,
|
|
114
|
+
// renderOpts
|
|
115
|
+
// );
|
|
100
116
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
117
|
+
// const inputSearch = getByRole("textbox");
|
|
118
|
+
// expect(queryByText("1 from a total of 2")).not.toBeInTheDocument();
|
|
119
|
+
// userEvent.type(inputSearch, "bank");
|
|
120
|
+
// expect(getByText("1 from a total of 2")).toBeInTheDocument();
|
|
121
|
+
// });
|
|
106
122
|
|
|
107
123
|
it("test Alias Name Mode functionality", async () => {
|
|
124
|
+
const childStructures = [
|
|
125
|
+
{ id: 1, name: "bank_capital" },
|
|
126
|
+
{ id: 2, name: "consumorenovables" },
|
|
127
|
+
];
|
|
108
128
|
const toggleStructuresAliasNameMode = jest.fn();
|
|
109
|
-
const props = { toggleStructuresAliasNameMode };
|
|
129
|
+
const props = { toggleStructuresAliasNameMode, childStructures };
|
|
110
130
|
const { getByRole } = render(<FilteredNav {...props} />, renderOpts);
|
|
111
131
|
|
|
112
132
|
userEvent.click(getByRole("button", { name: /enable/i }));
|
|
@@ -5,7 +5,7 @@ import { StructureItems } from "../StructureItems";
|
|
|
5
5
|
describe("<StructureItems />", () => {
|
|
6
6
|
it("matches the latest snapshot", () => {
|
|
7
7
|
const structures = [{ id: 1, name: "some structure" }];
|
|
8
|
-
const props = { structures };
|
|
8
|
+
const props = { structures, fnHasCatalogViewProp: jest.fn(), };
|
|
9
9
|
|
|
10
10
|
const { container } = render(<StructureItems {...props} />);
|
|
11
11
|
expect(container).toMatchSnapshot();
|
|
@@ -13,7 +13,7 @@ describe("<StructureItems />", () => {
|
|
|
13
13
|
|
|
14
14
|
it("matches the latest snapshot with alias", () => {
|
|
15
15
|
const structures = [{ id: 1, name: "some structure", alias: "alias" }];
|
|
16
|
-
const props = { structures };
|
|
16
|
+
const props = { structures, fnHasCatalogViewProp: jest.fn(), };
|
|
17
17
|
|
|
18
18
|
const { container } = render(<StructureItems {...props} />);
|
|
19
19
|
expect(container).toMatchSnapshot();
|
|
@@ -17,7 +17,7 @@ const renderOpts = {
|
|
|
17
17
|
describe("<StructureNav />", () => {
|
|
18
18
|
it("matches the latest snapshot", () => {
|
|
19
19
|
const childStructures = [{ id: 1 }];
|
|
20
|
-
const props = { childStructures };
|
|
20
|
+
const props = { childStructures, fnHasCatalogViewProp: jest.fn(),};
|
|
21
21
|
|
|
22
22
|
const { container } = render(<StructureNav {...props} />, renderOpts);
|
|
23
23
|
expect(container).toMatchSnapshot();
|
|
@@ -6,7 +6,11 @@ const structure1 = { id: 1, classes: { foo: "bar" } };
|
|
|
6
6
|
const structure2 = { id: 2, classes: null };
|
|
7
7
|
const structures = [structure1, structure2];
|
|
8
8
|
|
|
9
|
-
const props = {
|
|
9
|
+
const props = {
|
|
10
|
+
classifier: "foo",
|
|
11
|
+
structures,
|
|
12
|
+
fnHasCatalogViewProp: jest.fn(),
|
|
13
|
+
};
|
|
10
14
|
|
|
11
15
|
describe("<StructureNavClassified />", () => {
|
|
12
16
|
it("matches the latest snapshot", () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
1
2
|
import React from "react";
|
|
2
3
|
import { render } from "@truedat/test/render";
|
|
3
4
|
import { waitFor } from "@testing-library/react";
|
|
@@ -161,13 +162,14 @@ describe("<StructureStructureLinks />", () => {
|
|
|
161
162
|
const renderOpts = {
|
|
162
163
|
routes: ["/structures/60217/structureLinks"],
|
|
163
164
|
mocks: [linksMock],
|
|
165
|
+
state: { navFilter: { view: "SYSTEM_STRUCTURES", filter: {} } },
|
|
164
166
|
};
|
|
165
167
|
|
|
166
168
|
it("shows structure to structure links", async () => {
|
|
167
|
-
const { getByRole } = render(
|
|
168
|
-
...
|
|
169
|
-
state: { structureActions }
|
|
170
|
-
|
|
169
|
+
const { getByRole } = render(
|
|
170
|
+
<StructureStructureLinks {...props} />,
|
|
171
|
+
_.merge(renderOpts, { state: { structureActions } })
|
|
172
|
+
);
|
|
171
173
|
await waitFor(() => {
|
|
172
174
|
expect(getByRole("link", { name: /STRUCTURE_2/i })).toBeInTheDocument();
|
|
173
175
|
expect(getByRole("link", { name: /STRUCTURE_3/i })).toBeInTheDocument();
|
|
@@ -203,11 +205,10 @@ describe("<StructureStructureLinks />", () => {
|
|
|
203
205
|
const dispatch = jest.fn();
|
|
204
206
|
const { getByText, getByRole } = render(
|
|
205
207
|
<StructureStructureLinks {...props} />,
|
|
206
|
-
{
|
|
207
|
-
...renderOpts,
|
|
208
|
+
_.merge(renderOpts, {
|
|
208
209
|
state: { structureActions },
|
|
209
210
|
dispatch,
|
|
210
|
-
}
|
|
211
|
+
})
|
|
211
212
|
);
|
|
212
213
|
await waitFor(() => {
|
|
213
214
|
expect(getByRole("link", { name: /STRUCTURE_2/i })).toBeInTheDocument();
|
|
@@ -26,7 +26,7 @@ const renderOpts = {
|
|
|
26
26
|
|
|
27
27
|
describe("<SystemFilteredNav />", () => {
|
|
28
28
|
it("matches the latest snapshot", () => {
|
|
29
|
-
const { container } = render(<SystemFilteredNav />, renderOpts);
|
|
29
|
+
const { container } = render(<SystemFilteredNav fnHasCatalogViewProp={jest.fn()}/>, renderOpts);
|
|
30
30
|
expect(container).toMatchSnapshot();
|
|
31
31
|
});
|
|
32
32
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import useSWR from "swr";
|
|
2
|
+
import { apiJsonPost } from "@truedat/core/services/api";
|
|
3
|
+
import { API_BUCKET_PATHS } from "../api";
|
|
4
|
+
|
|
5
|
+
export const useBucketPaths = (navFilter) =>
|
|
6
|
+
useSWR(
|
|
7
|
+
navFilter.view === "BUCKET_VIEW"
|
|
8
|
+
? { url: API_BUCKET_PATHS, filters: navFilter.filter }
|
|
9
|
+
: null,
|
|
10
|
+
({ url }) => apiJsonPost(url, { filters: navFilter.filter })
|
|
11
|
+
);
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import useSWR from "swr";
|
|
2
2
|
import { apiJsonPost } from "@truedat/core/services/api";
|
|
3
3
|
import { API_BUCKET_STRUCTURES } from "../api";
|
|
4
4
|
|
|
5
|
-
export const useBucketStructures = () => {
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
export const useBucketStructures = ({ navFilter, parentFilter, ids, query }) => {
|
|
6
|
+
const params =
|
|
7
|
+
navFilter?.view === "SYSTEM_STRUCTURES"
|
|
8
|
+
? { filters: { ...navFilter.filter, ...parentFilter }, query }
|
|
9
|
+
: // BUCKET_VIEW searches by ids directly instead of navFilter and parentFilter
|
|
10
|
+
navFilter?.view === "BUCKET_VIEW"
|
|
11
|
+
? { filters: { ids }, query }
|
|
12
|
+
: undefined;
|
|
13
|
+
|
|
14
|
+
return useSWR({ url: API_BUCKET_STRUCTURES, params }, ({ url }) =>
|
|
15
|
+
apiJsonPost(url, params)
|
|
8
16
|
);
|
|
9
|
-
return mutation;
|
|
10
17
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { clearNavFilter, saveNavFilter } from "../routines";
|
|
2
2
|
|
|
3
|
-
export const initialState = {
|
|
3
|
+
export const initialState = {
|
|
4
|
+
view: "SYSTEM_STRUCTURES",
|
|
5
|
+
filter: {},
|
|
6
|
+
};
|
|
4
7
|
|
|
5
8
|
export const navFilter = (state = initialState, { type, payload }) => {
|
|
6
9
|
switch (type) {
|
|
@@ -6,6 +6,7 @@ const initialState = {};
|
|
|
6
6
|
const structureFilters = (state = initialState, { type, payload }) => {
|
|
7
7
|
switch (type) {
|
|
8
8
|
case clearStructureFilters.TRIGGER:
|
|
9
|
+
case fetchStructureFilters.TRIGGER:
|
|
9
10
|
return initialState;
|
|
10
11
|
case fetchStructureFilters.SUCCESS:
|
|
11
12
|
return _.flow(
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import { createSelector } from "reselect";
|
|
3
|
-
import { isBucketFilter } from "../utils/bucketNav";
|
|
4
3
|
|
|
5
4
|
const defaultName = "..";
|
|
6
5
|
|
|
@@ -23,7 +22,7 @@ export const getStructureParent = createSelector(
|
|
|
23
22
|
],
|
|
24
23
|
(structure, structureParents, navFilter) =>
|
|
25
24
|
_.isEmpty(structureParents)
|
|
26
|
-
? !_.isEmpty(structure) &&
|
|
25
|
+
? !_.isEmpty(structure) && navFilter?.view === "BUCKET_VIEW"
|
|
27
26
|
? { type: "bucket" }
|
|
28
27
|
: _.has("system")(structure)
|
|
29
28
|
? rootNavItem(structure)
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import { shallow } from "enzyme";
|
|
3
|
-
import { Input } from "semantic-ui-react";
|
|
4
|
-
import { intl } from "@truedat/test/intl-stub";
|
|
5
|
-
import { StructureSearch } from "../StructureSearch";
|
|
6
|
-
|
|
7
|
-
// workaround for enzyme issue with React.useContext
|
|
8
|
-
// see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
|
|
9
|
-
jest.spyOn(React, "useContext").mockImplementation(() => intl);
|
|
10
|
-
|
|
11
|
-
describe("<StructureSearch/>", () => {
|
|
12
|
-
it("matches the latest snapshot", () => {
|
|
13
|
-
const onChange = jest.fn();
|
|
14
|
-
const props = { onChange };
|
|
15
|
-
const wrapper = shallow(<StructureSearch {...props} />);
|
|
16
|
-
expect(wrapper).toMatchSnapshot();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it("dispatches onChange when Input changes", () => {
|
|
20
|
-
const onChange = jest.fn();
|
|
21
|
-
const props = { onChange };
|
|
22
|
-
const eventMock = {
|
|
23
|
-
target: {
|
|
24
|
-
value: {
|
|
25
|
-
toLowerCase: jest.fn()
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
const wrapper = shallow(<StructureSearch {...props} />);
|
|
30
|
-
expect(props.onChange.mock.calls.length).toBe(0);
|
|
31
|
-
wrapper.find(Input).simulate("change", eventMock);
|
|
32
|
-
expect(props.onChange.mock.calls.length).toBe(1);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
-
|
|
3
|
-
exports[`<StructureSearch/> matches the latest snapshot 1`] = `
|
|
4
|
-
<Input
|
|
5
|
-
fluid={true}
|
|
6
|
-
icon={
|
|
7
|
-
{
|
|
8
|
-
"link": true,
|
|
9
|
-
"name": "search",
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
iconPosition="left"
|
|
13
|
-
loading={false}
|
|
14
|
-
onChange={[Function]}
|
|
15
|
-
placeholder="structure.search.placeholder"
|
|
16
|
-
type="text"
|
|
17
|
-
/>
|
|
18
|
-
`;
|
package/src/utils/bucketNav.js
DELETED