@truedat/core 5.6.2 → 5.6.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.
- package/package.json +3 -3
- package/src/components/HierarchyFilterDropdown.js +116 -0
- package/src/components/SelectedFilters.js +8 -1
- package/src/components/__tests__/HierarchyFilterDropdown.spec.js +190 -0
- package/src/components/__tests__/SelectedFilters.spec.js +18 -1
- package/src/components/__tests__/__snapshots__/HierarchyFilterDropdown.spec.js.snap +99 -0
- package/src/services/filters.js +2 -2
- package/src/services/getHierarchyOptions.js +71 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/core",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.4",
|
|
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.6.
|
|
38
|
+
"@truedat/test": "5.6.4",
|
|
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": "
|
|
120
|
+
"gitHead": "fbecd33735354811f674678337ae16aaa15b4794"
|
|
121
121
|
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import PropTypes from "prop-types";
|
|
4
|
+
import { useHierarchy } from "@truedat/df/hooks/useHierarchies";
|
|
5
|
+
import {
|
|
6
|
+
getHierarchyOptions,
|
|
7
|
+
getKeyAndParents,
|
|
8
|
+
} from "../services/getHierarchyOptions";
|
|
9
|
+
import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
|
|
10
|
+
|
|
11
|
+
const PopulatedHierarchyFilterDropdown = (props) => {
|
|
12
|
+
const {
|
|
13
|
+
activeValues,
|
|
14
|
+
closeFilter,
|
|
15
|
+
filter,
|
|
16
|
+
loading,
|
|
17
|
+
openFilter,
|
|
18
|
+
options,
|
|
19
|
+
removeFilter,
|
|
20
|
+
toggleFilterValue,
|
|
21
|
+
} = props;
|
|
22
|
+
const hierarchyId = _.flow(
|
|
23
|
+
_.first,
|
|
24
|
+
_.prop("value"),
|
|
25
|
+
(value) => value.split("_"),
|
|
26
|
+
_.first
|
|
27
|
+
)(options);
|
|
28
|
+
|
|
29
|
+
const { data, error, loading: hierarchyLoading } = useHierarchy(hierarchyId);
|
|
30
|
+
if (error) return null;
|
|
31
|
+
if (hierarchyLoading) return null;
|
|
32
|
+
|
|
33
|
+
const hierarchyOptions = getHierarchyOptions(data?.nodes);
|
|
34
|
+
|
|
35
|
+
const includedKeys = _.flow(
|
|
36
|
+
_.map("value"),
|
|
37
|
+
_.flatMap(getKeyAndParents(hierarchyOptions)),
|
|
38
|
+
_.uniq
|
|
39
|
+
)(options);
|
|
40
|
+
|
|
41
|
+
const filterIncludedKeys = _.filter(({ key }) =>
|
|
42
|
+
_.includes(key)(includedKeys)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const filteredChildren = _.flow(_.prop("children"), filterIncludedKeys);
|
|
46
|
+
|
|
47
|
+
const filteredOptions = _.flow(
|
|
48
|
+
filterIncludedKeys,
|
|
49
|
+
_.map((node) => ({ ...node, children: filteredChildren(node) }))
|
|
50
|
+
)(hierarchyOptions);
|
|
51
|
+
|
|
52
|
+
const idActiveValues = _.map((key) =>
|
|
53
|
+
_.flow(_.find({ key }), _.prop("id"))(filteredOptions)
|
|
54
|
+
)(activeValues);
|
|
55
|
+
|
|
56
|
+
const handleToggleFilterValue = ({ filter, value }) => {
|
|
57
|
+
const getChildrenKeys = (id) => {
|
|
58
|
+
const { key, descendents } = _.flow(
|
|
59
|
+
_.find({ id }),
|
|
60
|
+
_.pick(["key", "descendents"])
|
|
61
|
+
)(filteredOptions);
|
|
62
|
+
const descendentKeys = _.map("key")(descendents);
|
|
63
|
+
return [key, ...descendentKeys];
|
|
64
|
+
};
|
|
65
|
+
const newValue = _.flow(
|
|
66
|
+
_.flatMap(getChildrenKeys),
|
|
67
|
+
_.uniq,
|
|
68
|
+
_.reject(_.isNil)
|
|
69
|
+
)(value);
|
|
70
|
+
toggleFilterValue({ filter, value: newValue });
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<FilterMultilevelDropdown
|
|
75
|
+
activeValues={idActiveValues}
|
|
76
|
+
closeFilter={closeFilter}
|
|
77
|
+
filter={filter}
|
|
78
|
+
loading={loading}
|
|
79
|
+
openFilter={openFilter}
|
|
80
|
+
options={filteredOptions}
|
|
81
|
+
removeFilter={removeFilter}
|
|
82
|
+
toggleFilterValue={handleToggleFilterValue}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
PopulatedHierarchyFilterDropdown.propTypes = {
|
|
88
|
+
activeValues: PropTypes.array,
|
|
89
|
+
closeFilter: PropTypes.func,
|
|
90
|
+
filter: PropTypes.string,
|
|
91
|
+
loading: PropTypes.bool,
|
|
92
|
+
openFilter: PropTypes.func,
|
|
93
|
+
options: PropTypes.array,
|
|
94
|
+
removeFilter: PropTypes.func,
|
|
95
|
+
toggleFilterValue: PropTypes.func,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const HierarchyFilterDropdown = (props) =>
|
|
99
|
+
_.isEmpty(props.options) ? (
|
|
100
|
+
<FilterMultilevelDropdown {...props} />
|
|
101
|
+
) : (
|
|
102
|
+
<PopulatedHierarchyFilterDropdown {...props} />
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
HierarchyFilterDropdown.propTypes = {
|
|
106
|
+
activeValues: PropTypes.array,
|
|
107
|
+
closeFilter: PropTypes.func,
|
|
108
|
+
filter: PropTypes.string,
|
|
109
|
+
loading: PropTypes.bool,
|
|
110
|
+
openFilter: PropTypes.func,
|
|
111
|
+
options: PropTypes.array,
|
|
112
|
+
removeFilter: PropTypes.func,
|
|
113
|
+
toggleFilterValue: PropTypes.func,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export default HierarchyFilterDropdown;
|
|
@@ -4,6 +4,7 @@ import PropTypes from "prop-types";
|
|
|
4
4
|
import { FormattedMessage } from "react-intl";
|
|
5
5
|
import FilterDropdown from "./FilterDropdown";
|
|
6
6
|
import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
|
|
7
|
+
import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
|
|
7
8
|
import ModalSaveFilter from "./ModalSaveFilter";
|
|
8
9
|
import UserFilters from "./UserFilters";
|
|
9
10
|
|
|
@@ -19,6 +20,7 @@ export const SelectedFilters = ({
|
|
|
19
20
|
saveFilters,
|
|
20
21
|
selectedFilter,
|
|
21
22
|
selectedFilters,
|
|
23
|
+
filterTypes,
|
|
22
24
|
selectedFilterActiveValues: activeValues,
|
|
23
25
|
selectedFilterValues,
|
|
24
26
|
selectedUserFilter,
|
|
@@ -45,9 +47,11 @@ export const SelectedFilters = ({
|
|
|
45
47
|
<FormattedMessage id="search.applied_filters" />
|
|
46
48
|
</div>
|
|
47
49
|
{selectedFilters.map((filter) => {
|
|
50
|
+
const filterType = _.prop(filter)(filterTypes);
|
|
48
51
|
const options = _.isEqual(filter, selectedFilter)
|
|
49
52
|
? selectedFilterValues
|
|
50
53
|
: null;
|
|
54
|
+
|
|
51
55
|
const props = {
|
|
52
56
|
key: filter,
|
|
53
57
|
activeValues,
|
|
@@ -59,8 +63,10 @@ export const SelectedFilters = ({
|
|
|
59
63
|
removeFilter,
|
|
60
64
|
toggleFilterValue,
|
|
61
65
|
};
|
|
62
|
-
return
|
|
66
|
+
return filterType === "domain" ? (
|
|
63
67
|
<FilterMultilevelDropdown {...props} />
|
|
68
|
+
) : filterType === "hierarchy" ? (
|
|
69
|
+
<HierarchyFilterDropdown {...props} />
|
|
64
70
|
) : (
|
|
65
71
|
<FilterDropdown {...props} />
|
|
66
72
|
);
|
|
@@ -95,6 +101,7 @@ SelectedFilters.propTypes = {
|
|
|
95
101
|
selectedFilterActiveValues: PropTypes.array,
|
|
96
102
|
selectedFilterValues: PropTypes.array,
|
|
97
103
|
selectedFilters: PropTypes.array,
|
|
104
|
+
filterTypes: PropTypes.object,
|
|
98
105
|
selectedUserFilter: PropTypes.string,
|
|
99
106
|
toggleFilterValue: PropTypes.func,
|
|
100
107
|
userFilters: PropTypes.array,
|
|
@@ -0,0 +1,190 @@
|
|
|
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 HierarchyFilterDropdown from "../HierarchyFilterDropdown";
|
|
6
|
+
|
|
7
|
+
const hierarchy = {
|
|
8
|
+
id: 123,
|
|
9
|
+
nodes: [
|
|
10
|
+
{
|
|
11
|
+
id: 1,
|
|
12
|
+
key: "123_1",
|
|
13
|
+
parentId: null,
|
|
14
|
+
name: "foo",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 2,
|
|
18
|
+
key: "123_2",
|
|
19
|
+
parentId: 1,
|
|
20
|
+
name: "bar",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 3,
|
|
24
|
+
key: "123_3",
|
|
25
|
+
parentId: null,
|
|
26
|
+
name: "baz",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
jest.mock("@truedat/df/hooks/useHierarchies", () => {
|
|
32
|
+
const originalModule = jest.requireActual("@truedat/df/hooks/useHierarchies");
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
__esModule: true,
|
|
36
|
+
...originalModule,
|
|
37
|
+
useHierarchy: jest.fn(() => ({
|
|
38
|
+
data: hierarchy,
|
|
39
|
+
loading: false,
|
|
40
|
+
})),
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("<HierarchyFilterDropdown />", () => {
|
|
45
|
+
const toggleFilterValue = jest.fn();
|
|
46
|
+
const openFilter = jest.fn();
|
|
47
|
+
const closeFilter = jest.fn();
|
|
48
|
+
const removeFilter = jest.fn();
|
|
49
|
+
const options = [
|
|
50
|
+
{
|
|
51
|
+
value: "123_2",
|
|
52
|
+
text: "bar",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
const filter = "hierarchy_field";
|
|
56
|
+
const props = {
|
|
57
|
+
filter,
|
|
58
|
+
closeFilter,
|
|
59
|
+
options,
|
|
60
|
+
openFilter,
|
|
61
|
+
toggleFilterValue,
|
|
62
|
+
loading: false,
|
|
63
|
+
removeFilter,
|
|
64
|
+
filterTypes: { hierarchy_field: "hierarchy" },
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const messages = {
|
|
68
|
+
en: {
|
|
69
|
+
"filter.hierarchy_field": "Hierarchy Field",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
it("matches the latest snapshot", () => {
|
|
74
|
+
const { container } = render(<HierarchyFilterDropdown {...props} />, {
|
|
75
|
+
messages,
|
|
76
|
+
});
|
|
77
|
+
expect(container).toMatchSnapshot();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("matches the latest snapshot for empty options", () => {
|
|
81
|
+
const customProps = {
|
|
82
|
+
...props,
|
|
83
|
+
options: [],
|
|
84
|
+
};
|
|
85
|
+
const { container } = render(<HierarchyFilterDropdown {...customProps} />, {
|
|
86
|
+
messages,
|
|
87
|
+
});
|
|
88
|
+
expect(container).toMatchSnapshot();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("handles the selection of a colapsed element", async () => {
|
|
92
|
+
const { rerender, getByRole, container, queryByText } = render(
|
|
93
|
+
<HierarchyFilterDropdown {...props} />,
|
|
94
|
+
{
|
|
95
|
+
messages,
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
userEvent.click(getByRole("listbox"));
|
|
100
|
+
|
|
101
|
+
userEvent.click(container.querySelector('[class="plus icon"]'));
|
|
102
|
+
await waitFor(() => {
|
|
103
|
+
expect(getByRole("option", { name: /bar/i })).toBeTruthy();
|
|
104
|
+
});
|
|
105
|
+
userEvent.click(container.querySelector('[class="minus icon"]'));
|
|
106
|
+
await waitFor(() => {
|
|
107
|
+
expect(queryByText(/bar/)).toBeFalsy();
|
|
108
|
+
});
|
|
109
|
+
userEvent.click(container.querySelector('[class="plus icon"]'));
|
|
110
|
+
// Select value
|
|
111
|
+
userEvent.click(getByRole("option", { name: /bar/i }));
|
|
112
|
+
await waitFor(() => {
|
|
113
|
+
expect(toggleFilterValue).toHaveBeenCalledWith({
|
|
114
|
+
filter,
|
|
115
|
+
value: ["123_2"],
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
rerender(
|
|
119
|
+
<HierarchyFilterDropdown {...{ ...props, activeValues: ["123_2"] }} />
|
|
120
|
+
);
|
|
121
|
+
// Unselect
|
|
122
|
+
userEvent.click(getByRole("option", { name: /bar/i }));
|
|
123
|
+
await waitFor(() => {
|
|
124
|
+
expect(toggleFilterValue).toHaveBeenCalledWith({ filter, value: [] });
|
|
125
|
+
});
|
|
126
|
+
// Select parent
|
|
127
|
+
rerender(<HierarchyFilterDropdown {...{ ...props, activeValues: [] }} />);
|
|
128
|
+
userEvent.click(getByRole("option", { name: /foo/i }));
|
|
129
|
+
await waitFor(() => {
|
|
130
|
+
expect(toggleFilterValue).toHaveBeenCalledWith({
|
|
131
|
+
filter,
|
|
132
|
+
value: ["123_1", "123_2"],
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
// Unselect parent by selecting children
|
|
136
|
+
rerender(
|
|
137
|
+
<HierarchyFilterDropdown
|
|
138
|
+
{...{ ...props, activeValues: ["123_1", "123_2"] }}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
userEvent.click(getByRole("option", { name: /bar/i }));
|
|
142
|
+
await waitFor(() => {
|
|
143
|
+
expect(toggleFilterValue).toHaveBeenCalledWith({
|
|
144
|
+
filter,
|
|
145
|
+
value: ["123_2"],
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
// Delete filter
|
|
149
|
+
userEvent.click(container.querySelector('[class="delete icon"]'));
|
|
150
|
+
await waitFor(() => {
|
|
151
|
+
expect(removeFilter).toBeCalledTimes(1);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("handles the selection of searched elements", async () => {
|
|
156
|
+
const customProps = {
|
|
157
|
+
...props,
|
|
158
|
+
options: [
|
|
159
|
+
{
|
|
160
|
+
value: "123_2",
|
|
161
|
+
text: "bar",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
value: "123_3",
|
|
165
|
+
text: "baz",
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
};
|
|
169
|
+
const { container, getByRole } = render(
|
|
170
|
+
<HierarchyFilterDropdown {...customProps} />,
|
|
171
|
+
{
|
|
172
|
+
messages,
|
|
173
|
+
}
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
userEvent.click(getByRole("listbox"));
|
|
177
|
+
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
expect(getByRole("option", { name: /foo/i })).toBeTruthy();
|
|
180
|
+
expect(getByRole("option", { name: /baz/i })).toBeTruthy();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const input = container.querySelector('[type="text"]');
|
|
184
|
+
userEvent.type(input, "bar");
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
expect(getByRole("option", { name: /bar/i })).toBeTruthy();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -5,6 +5,9 @@ import { SelectedFilters } from "../SelectedFilters";
|
|
|
5
5
|
describe("<SelectedFilters/>", () => {
|
|
6
6
|
const getProps = () => ({
|
|
7
7
|
resetFilters: jest.fn(),
|
|
8
|
+
filterTypes: {
|
|
9
|
+
foo: "fooType",
|
|
10
|
+
},
|
|
8
11
|
selectedFilter: "foo",
|
|
9
12
|
selectedFilters: ["foo", "bar"],
|
|
10
13
|
selectedFilterActiveValues: ["value2"],
|
|
@@ -56,15 +59,29 @@ describe("<SelectedFilters/>", () => {
|
|
|
56
59
|
expect(wrapper.find("UserFilters")).toHaveLength(1);
|
|
57
60
|
});
|
|
58
61
|
|
|
59
|
-
it("renders FilterMultilevelDropdown if filter is
|
|
62
|
+
it("renders FilterMultilevelDropdown if filter is of type domain", () => {
|
|
60
63
|
const props = getProps();
|
|
61
64
|
const customProps = {
|
|
62
65
|
...props,
|
|
63
66
|
selectedFilters: ["taxonomy"],
|
|
64
67
|
selectedFilter: "taxonomy",
|
|
68
|
+
filterTypes: { taxonomy: "domain" },
|
|
65
69
|
selectedFilterValues: [{ id: 1, name: "foo", level: 0 }],
|
|
66
70
|
};
|
|
67
71
|
const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
|
|
68
72
|
expect(wrapper.find("FilterMultilevelDropdown")).toHaveLength(1);
|
|
69
73
|
});
|
|
74
|
+
|
|
75
|
+
it("renders HierarchyFilterDropdown if filter is of type hierarchy", () => {
|
|
76
|
+
const props = getProps();
|
|
77
|
+
const customProps = {
|
|
78
|
+
...props,
|
|
79
|
+
selectedFilters: ["hierarchy_field"],
|
|
80
|
+
selectedFilter: "hierarchy_field",
|
|
81
|
+
filterTypes: { hierarchy_field: "hierarchy" },
|
|
82
|
+
selectedFilterValues: [{ key: "2_1", id: 1, name: "foo", level: 0 }],
|
|
83
|
+
};
|
|
84
|
+
const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
|
|
85
|
+
expect(wrapper.find("HierarchyFilterDropdown")).toHaveLength(1);
|
|
86
|
+
});
|
|
70
87
|
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<HierarchyFilterDropdown /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
aria-expanded="true"
|
|
7
|
+
class="ui active visible floating item dropdown"
|
|
8
|
+
role="listbox"
|
|
9
|
+
tabindex="0"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="ui label"
|
|
13
|
+
>
|
|
14
|
+
hierarchy_field
|
|
15
|
+
<i
|
|
16
|
+
aria-hidden="true"
|
|
17
|
+
class="delete icon"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
<div
|
|
21
|
+
class="menu transition dimmable visible"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
class="ui left icon input search"
|
|
25
|
+
>
|
|
26
|
+
<input
|
|
27
|
+
type="text"
|
|
28
|
+
/>
|
|
29
|
+
<i
|
|
30
|
+
aria-hidden="true"
|
|
31
|
+
class="search icon"
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
<div
|
|
35
|
+
class="scrolling menu transition"
|
|
36
|
+
>
|
|
37
|
+
<div
|
|
38
|
+
aria-selected="false"
|
|
39
|
+
class="item"
|
|
40
|
+
role="option"
|
|
41
|
+
>
|
|
42
|
+
<div
|
|
43
|
+
style="margin-left: 0px;"
|
|
44
|
+
>
|
|
45
|
+
<i
|
|
46
|
+
aria-hidden="true"
|
|
47
|
+
class="plus icon"
|
|
48
|
+
/>
|
|
49
|
+
<i
|
|
50
|
+
aria-hidden="true"
|
|
51
|
+
class="square outline icon"
|
|
52
|
+
/>
|
|
53
|
+
foo
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
exports[`<HierarchyFilterDropdown /> matches the latest snapshot for empty options 1`] = `
|
|
63
|
+
<div>
|
|
64
|
+
<div
|
|
65
|
+
aria-expanded="false"
|
|
66
|
+
class="ui floating item dropdown"
|
|
67
|
+
role="listbox"
|
|
68
|
+
tabindex="0"
|
|
69
|
+
>
|
|
70
|
+
<div
|
|
71
|
+
class="ui label"
|
|
72
|
+
>
|
|
73
|
+
hierarchy_field
|
|
74
|
+
<i
|
|
75
|
+
aria-hidden="true"
|
|
76
|
+
class="delete icon"
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
<div
|
|
80
|
+
class="menu transition dimmable"
|
|
81
|
+
>
|
|
82
|
+
<div
|
|
83
|
+
class="ui left icon input search"
|
|
84
|
+
>
|
|
85
|
+
<input
|
|
86
|
+
type="text"
|
|
87
|
+
/>
|
|
88
|
+
<i
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
class="search icon"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
<div
|
|
94
|
+
class="scrolling menu transition"
|
|
95
|
+
/>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
`;
|
package/src/services/filters.js
CHANGED
|
@@ -30,8 +30,8 @@ export const getSearchPayload = (searchQuery, props) => {
|
|
|
30
30
|
|
|
31
31
|
export const isEmbedded = _.overEvery(_.has("id"), _.has("name"));
|
|
32
32
|
|
|
33
|
-
export const formatFilterValues = (
|
|
34
|
-
|
|
33
|
+
export const formatFilterValues = ({ values, type }) =>
|
|
34
|
+
type === "domain"
|
|
35
35
|
? getDomainSelectorOptions({ domains: values })
|
|
36
36
|
: _.any(isEmbedded)(values)
|
|
37
37
|
? _.map(({ id, name }) => ({ value: id, text: name }))(values)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import _ from "lodash/fp";
|
|
2
|
+
import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
|
|
3
|
+
|
|
4
|
+
const isChildOf =
|
|
5
|
+
({ id }) =>
|
|
6
|
+
({ parentId }) =>
|
|
7
|
+
parentId === id;
|
|
8
|
+
const isParentOf =
|
|
9
|
+
({ parentId }) =>
|
|
10
|
+
({ id }) =>
|
|
11
|
+
parentId === id;
|
|
12
|
+
|
|
13
|
+
const hasNoParentIn = (domains) => (domain) =>
|
|
14
|
+
!_.some(isParentOf(domain))(domains);
|
|
15
|
+
|
|
16
|
+
const findParentsIn = (domains) => (child) =>
|
|
17
|
+
_.filter(isParentOf(child))(domains);
|
|
18
|
+
const findChildrenIn = (domains) => (parent) =>
|
|
19
|
+
_.filter(isChildOf(parent))(domains);
|
|
20
|
+
|
|
21
|
+
const findDescendents = (parents) => (domains) => {
|
|
22
|
+
const children = _.flatMap(findChildrenIn(domains))(parents);
|
|
23
|
+
return _.isEmpty(children)
|
|
24
|
+
? children
|
|
25
|
+
: _.concat(children, findDescendents(children)(domains));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const findAncestorsIn = (domains) => (children) => {
|
|
29
|
+
const parents = _.flatMap(findParentsIn(domains))(children);
|
|
30
|
+
return _.isEmpty(parents)
|
|
31
|
+
? parents
|
|
32
|
+
: _.concat(findAncestorsIn(domains)(parents), parents);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const reduceTaxonomy = (domains) => (domainsInLevel, level) => {
|
|
36
|
+
if (domains)
|
|
37
|
+
return _.reduce((acc, domain) => {
|
|
38
|
+
const children = _.sortBy(["name"])(findChildrenIn(domains)(domain));
|
|
39
|
+
return [
|
|
40
|
+
...acc,
|
|
41
|
+
{
|
|
42
|
+
...domain,
|
|
43
|
+
ancestors: findAncestorsIn(domains)([domain]),
|
|
44
|
+
descendents: _.sortBy(["name"])(findDescendents([domain])(domains)),
|
|
45
|
+
children,
|
|
46
|
+
level,
|
|
47
|
+
},
|
|
48
|
+
...reduceTaxonomy(domains)(children, level + 1),
|
|
49
|
+
];
|
|
50
|
+
}, [])(domainsInLevel);
|
|
51
|
+
return [];
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const getHierarchyOptions = (nodes) => {
|
|
55
|
+
const roots = _.flow(
|
|
56
|
+
_.filter(hasNoParentIn(nodes)),
|
|
57
|
+
_.sortBy(accentInsensitivePathOrder("name"))
|
|
58
|
+
)(nodes);
|
|
59
|
+
return reduceTaxonomy(nodes)(roots, 0);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const getKeyAndParents = (hierarchyOptions) => (key) => {
|
|
63
|
+
const parentKeys = _.flow(
|
|
64
|
+
_.find({ key }),
|
|
65
|
+
_.getOr([], "ancestors"),
|
|
66
|
+
_.flatMap(({ key }) => getKeyAndParents(hierarchyOptions)(key))
|
|
67
|
+
)(hierarchyOptions);
|
|
68
|
+
return [...parentKeys, key];
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export { getHierarchyOptions, getKeyAndParents };
|