@truedat/core 4.46.3 → 4.46.6
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 +21 -0
- package/package.json +3 -3
- package/src/components/CardGroupsAccordion.js +145 -0
- package/src/components/FiltersLoader.js +2 -1
- package/src/components/OptionGroup.js +52 -24
- package/src/components/SelectedFilters.js +64 -55
- package/src/components/__tests__/CardGroupsAccordion.spec.js +142 -0
- package/src/components/__tests__/__snapshots__/CardGroupsAccordion.spec.js.snap +154 -0
- package/src/components/__tests__/__snapshots__/OptionGroup.spec.js.snap +2 -3
- package/src/components/index.js +2 -0
- package/src/messages/en.js +1 -0
- package/src/messages/es.js +1 -0
- package/src/routes.js +2 -2
- package/src/selectors/makeActiveFiltersSelector.js +2 -2
- package/src/services/__tests__/filters.spec.js +14 -6
- package/src/services/filters.js +7 -7
- package/src/styles/CardGroupsAccordion.less +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.46.6] 2022-06-20
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- [TD-4858] Added CardGroupsAccordion component to aggrupate cards in different
|
|
8
|
+
groups
|
|
9
|
+
|
|
10
|
+
## [4.46.5] 2022-06-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- [TD-4894] Multiple column operator in implementation creation
|
|
15
|
+
|
|
16
|
+
## [4.46.4] 2022-06-16
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- [TD-4720] When merging default filters and user-specified filters, the
|
|
21
|
+
`taxonomy` filter specified now overrides the default value. This change was
|
|
22
|
+
needed for the domain filter to work in the `<DomainConcepts />` tab.
|
|
23
|
+
|
|
3
24
|
## [4.46.2] 2022-06-16
|
|
4
25
|
|
|
5
26
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@truedat/core",
|
|
3
|
-
"version": "4.46.
|
|
3
|
+
"version": "4.46.6",
|
|
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.4",
|
|
36
36
|
"@testing-library/react": "^12.0.0",
|
|
37
37
|
"@testing-library/user-event": "^13.2.1",
|
|
38
|
-
"@truedat/test": "4.46.
|
|
38
|
+
"@truedat/test": "4.46.6",
|
|
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",
|
|
@@ -112,5 +112,5 @@
|
|
|
112
112
|
"react-dom": ">= 16.8.6 < 17",
|
|
113
113
|
"semantic-ui-react": ">= 0.88.2 < 2.1"
|
|
114
114
|
},
|
|
115
|
-
"gitHead": "
|
|
115
|
+
"gitHead": "9eced5bbfbd39d1709c31976d3879f6c5688f83f"
|
|
116
116
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import PropTypes from "prop-types";
|
|
3
|
+
import _ from "lodash/fp";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
|
|
6
|
+
import { Accordion, Icon, Header, Card } from "semantic-ui-react";
|
|
7
|
+
|
|
8
|
+
import "../styles/CardGroupsAccordion.less";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Component to split Card.Groups into different folding sections
|
|
12
|
+
*
|
|
13
|
+
* If an element doesn't have a df_content.system_group, it must be appear
|
|
14
|
+
* under the "default" key.
|
|
15
|
+
*
|
|
16
|
+
* The objects that are inside the list of each group must contain the keys
|
|
17
|
+
* that will be passed as properties to cardComponent
|
|
18
|
+
*
|
|
19
|
+
* @component
|
|
20
|
+
* @example
|
|
21
|
+
*
|
|
22
|
+
* import { Card } from "semantic-ui-react";
|
|
23
|
+
*
|
|
24
|
+
*
|
|
25
|
+
* const groups = {
|
|
26
|
+
* foo: [
|
|
27
|
+
* {
|
|
28
|
+
* title: "hello foo",
|
|
29
|
+
* meta: "world foo",
|
|
30
|
+
* description: "Test description foo 1",
|
|
31
|
+
* },
|
|
32
|
+
* {
|
|
33
|
+
* title: "hello foo 2",
|
|
34
|
+
* meta: "world foo 2",
|
|
35
|
+
* description: "Test description foo 2",
|
|
36
|
+
* },
|
|
37
|
+
* ],
|
|
38
|
+
* bar: [
|
|
39
|
+
* {
|
|
40
|
+
* title: "hello bar",
|
|
41
|
+
* meta: "world bar",
|
|
42
|
+
* description: "Test description bar 1",
|
|
43
|
+
* },
|
|
44
|
+
* {
|
|
45
|
+
* title: "hello bar 2",
|
|
46
|
+
* meta: "world bar 2",
|
|
47
|
+
* description: "Test description bar 2",
|
|
48
|
+
* },
|
|
49
|
+
* ],
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* const CustomCard = (props) => (
|
|
53
|
+
* <Card>
|
|
54
|
+
* <Card.Content>
|
|
55
|
+
* <Card.Header>{props.title}</Card.Header>
|
|
56
|
+
* <Card.Meta>
|
|
57
|
+
* <span>{props.meta}</span>
|
|
58
|
+
* </Card.Meta>
|
|
59
|
+
* <Card.Description>{props.description}</Card.Description>
|
|
60
|
+
* </Card.Content>
|
|
61
|
+
* </Card>
|
|
62
|
+
* );
|
|
63
|
+
*
|
|
64
|
+
* return (
|
|
65
|
+
* <CardGroupsAccordion groups={groups} cardComponent={CustomCard} />
|
|
66
|
+
* )
|
|
67
|
+
*/
|
|
68
|
+
const CardGroupsAccordion = ({ groups, cardComponent: CardComponent }) => {
|
|
69
|
+
const { formatMessage } = useIntl();
|
|
70
|
+
|
|
71
|
+
// State and lifecycle methods
|
|
72
|
+
|
|
73
|
+
const [cardGroups, setCardGroups] = _.flow(
|
|
74
|
+
_.toPairs,
|
|
75
|
+
_.reduce(
|
|
76
|
+
(acc, [groupName, members]) => ({
|
|
77
|
+
...acc,
|
|
78
|
+
[groupName]: { members, active: true },
|
|
79
|
+
}),
|
|
80
|
+
{}
|
|
81
|
+
),
|
|
82
|
+
useState
|
|
83
|
+
)(groups);
|
|
84
|
+
|
|
85
|
+
// Handlers
|
|
86
|
+
|
|
87
|
+
const handleClick = (e, { index: groupName }) =>
|
|
88
|
+
_.flow(
|
|
89
|
+
_.set(`${groupName}.active`, !cardGroups[groupName].active),
|
|
90
|
+
setCardGroups
|
|
91
|
+
)(cardGroups);
|
|
92
|
+
|
|
93
|
+
// Renders
|
|
94
|
+
|
|
95
|
+
const renderSection = (groupName, group) => (
|
|
96
|
+
<div key={groupName}>
|
|
97
|
+
<Accordion.Title
|
|
98
|
+
active={group.active}
|
|
99
|
+
index={groupName}
|
|
100
|
+
onClick={handleClick}
|
|
101
|
+
className="CardGroupTitle"
|
|
102
|
+
>
|
|
103
|
+
<Header as="h3" dividing>
|
|
104
|
+
<Icon name="dropdown" />
|
|
105
|
+
{groupName === "default"
|
|
106
|
+
? formatMessage({
|
|
107
|
+
id: "components.CardGroupsAccordion.withoutGroups",
|
|
108
|
+
})
|
|
109
|
+
: groupName}
|
|
110
|
+
</Header>
|
|
111
|
+
</Accordion.Title>
|
|
112
|
+
<Accordion.Content active={group.active}>
|
|
113
|
+
<Card.Group>
|
|
114
|
+
{group.members.map((cardProps) => (
|
|
115
|
+
<CardComponent key={cardProps.id} {...cardProps} />
|
|
116
|
+
))}
|
|
117
|
+
</Card.Group>
|
|
118
|
+
</Accordion.Content>
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<Accordion exclusive={false}>
|
|
124
|
+
{cardGroups?.default
|
|
125
|
+
? renderSection("default", cardGroups.default)
|
|
126
|
+
: null}
|
|
127
|
+
{_.flow(
|
|
128
|
+
_.omit(["default"]),
|
|
129
|
+
_.toPairs,
|
|
130
|
+
_.sortBy(0),
|
|
131
|
+
_.map(([groupName, group]) => renderSection(groupName, group))
|
|
132
|
+
)(cardGroups)}
|
|
133
|
+
</Accordion>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
CardGroupsAccordion.propTypes = {
|
|
138
|
+
/** Object where the key is the name of group and the value is an array with
|
|
139
|
+
* card props that will be used in cardComponent */
|
|
140
|
+
groups: PropTypes.object,
|
|
141
|
+
/** Component to render Card content using card props passed in groups */
|
|
142
|
+
cardComponent: PropTypes.func,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export default CardGroupsAccordion;
|
|
@@ -25,13 +25,14 @@ export const FiltersLoader = ({
|
|
|
25
25
|
}
|
|
26
26
|
: _.omit([selectedFilter])({ ...defaultFilters, ...filters });
|
|
27
27
|
fetchFilters({ filters: mergedFilters });
|
|
28
|
-
}, [fetchFilters, filters, selectedFilter]);
|
|
28
|
+
}, [fetchFilters, filters, selectedFilter, defaultFilters]);
|
|
29
29
|
return null;
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
FiltersLoader.propTypes = {
|
|
33
33
|
clearFilters: PropTypes.func.isRequired,
|
|
34
34
|
clearSort: PropTypes.func,
|
|
35
|
+
defaultFilters: PropTypes.object,
|
|
35
36
|
fetchFilters: PropTypes.func.isRequired,
|
|
36
37
|
filters: PropTypes.object.isRequired,
|
|
37
38
|
selectedFilter: PropTypes.string,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React, { useState } from "react";
|
|
2
|
+
import React, { useState, useEffect } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { Dropdown } from "semantic-ui-react";
|
|
5
5
|
import { lowerDeburr } from "../services/sort";
|
|
@@ -27,9 +27,12 @@ export const OptionGroup = ({
|
|
|
27
27
|
options = [],
|
|
28
28
|
placeholder = "Select",
|
|
29
29
|
onClick,
|
|
30
|
+
onChange,
|
|
30
31
|
value,
|
|
32
|
+
multiple,
|
|
31
33
|
}) => {
|
|
32
34
|
const [availableOptions, setOptions] = useState(options);
|
|
35
|
+
const [flattenedOptions, setFlattenedOptions] = useState();
|
|
33
36
|
const [open, openMenu] = useState(false);
|
|
34
37
|
const [query, setQuery] = useState("");
|
|
35
38
|
|
|
@@ -55,6 +58,14 @@ export const OptionGroup = ({
|
|
|
55
58
|
);
|
|
56
59
|
};
|
|
57
60
|
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
// eslint-disable-next-line prettier/prettier
|
|
63
|
+
_.flow(
|
|
64
|
+
toFlattenedShorthands,
|
|
65
|
+
setFlattenedOptions
|
|
66
|
+
)(availableOptions);
|
|
67
|
+
}, [availableOptions]);
|
|
68
|
+
|
|
58
69
|
const flatOptionMatches = (o, searchQuery) =>
|
|
59
70
|
!_.has("options")(o) && optionMatches(lowerDeburr(searchQuery))(o);
|
|
60
71
|
|
|
@@ -76,6 +87,10 @@ export const OptionGroup = ({
|
|
|
76
87
|
onClick(values);
|
|
77
88
|
};
|
|
78
89
|
|
|
90
|
+
const onChangeOptionsGroup = (e, dropdown) => {
|
|
91
|
+
multiple && onChange(dropdown.value);
|
|
92
|
+
};
|
|
93
|
+
|
|
79
94
|
const findText = () =>
|
|
80
95
|
_.flow(
|
|
81
96
|
_.reduce(
|
|
@@ -89,10 +104,43 @@ export const OptionGroup = ({
|
|
|
89
104
|
|
|
90
105
|
const text = value && !query && findText();
|
|
91
106
|
|
|
107
|
+
const toFlattenedShorthands = (availableOpts) =>
|
|
108
|
+
availableOpts.flatMap((option, i) => {
|
|
109
|
+
const result = _.has("options")(option)
|
|
110
|
+
? [
|
|
111
|
+
{
|
|
112
|
+
key: i,
|
|
113
|
+
children: (
|
|
114
|
+
<>
|
|
115
|
+
<Dropdown.Divider />
|
|
116
|
+
<Dropdown.Header icon={option.icon} content={option.label} />
|
|
117
|
+
</>
|
|
118
|
+
),
|
|
119
|
+
},
|
|
120
|
+
...option.options.map((innerOption, i) => ({
|
|
121
|
+
...innerOption,
|
|
122
|
+
className: "group",
|
|
123
|
+
onClick: doOnClick,
|
|
124
|
+
})),
|
|
125
|
+
]
|
|
126
|
+
: [
|
|
127
|
+
{
|
|
128
|
+
...option,
|
|
129
|
+
className: "header",
|
|
130
|
+
onClick: doOnClick,
|
|
131
|
+
},
|
|
132
|
+
];
|
|
133
|
+
return result;
|
|
134
|
+
});
|
|
135
|
+
|
|
92
136
|
return (
|
|
93
137
|
<>
|
|
94
138
|
{label && <label>{label}</label>}
|
|
139
|
+
|
|
95
140
|
<Dropdown
|
|
141
|
+
multiple={multiple}
|
|
142
|
+
options={flattenedOptions}
|
|
143
|
+
onChange={onChangeOptionsGroup}
|
|
96
144
|
fluid={fluid}
|
|
97
145
|
className="selection"
|
|
98
146
|
placeholder={placeholder}
|
|
@@ -106,41 +154,21 @@ export const OptionGroup = ({
|
|
|
106
154
|
onBlur={() => openMenu(false)}
|
|
107
155
|
text={text}
|
|
108
156
|
value={value}
|
|
109
|
-
|
|
110
|
-
<Dropdown.Menu>
|
|
111
|
-
{availableOptions.map((option, i) =>
|
|
112
|
-
_.has("options")(option) ? (
|
|
113
|
-
<Group
|
|
114
|
-
key={i}
|
|
115
|
-
value={value}
|
|
116
|
-
onClick={doOnClick}
|
|
117
|
-
options={_.prop("options")(option)}
|
|
118
|
-
{...option}
|
|
119
|
-
/>
|
|
120
|
-
) : (
|
|
121
|
-
<Dropdown.Item
|
|
122
|
-
key={i}
|
|
123
|
-
{...option}
|
|
124
|
-
onClick={doOnClick}
|
|
125
|
-
className="header"
|
|
126
|
-
selected={value == option.value}
|
|
127
|
-
/>
|
|
128
|
-
)
|
|
129
|
-
)}
|
|
130
|
-
</Dropdown.Menu>
|
|
131
|
-
</Dropdown>
|
|
157
|
+
/>
|
|
132
158
|
</>
|
|
133
159
|
);
|
|
134
160
|
};
|
|
135
161
|
|
|
136
162
|
OptionGroup.propTypes = {
|
|
137
163
|
onClick: PropTypes.func,
|
|
164
|
+
onChange: PropTypes.func,
|
|
138
165
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
|
139
166
|
fluid: PropTypes.bool,
|
|
140
167
|
inline: PropTypes.bool,
|
|
141
168
|
label: PropTypes.string,
|
|
142
169
|
placeholder: PropTypes.string,
|
|
143
170
|
options: PropTypes.array,
|
|
171
|
+
multiple: PropTypes.bool,
|
|
144
172
|
};
|
|
145
173
|
|
|
146
174
|
export default OptionGroup;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useEffect } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { FormattedMessage } from "react-intl";
|
|
5
5
|
import FilterDropdown from "./FilterDropdown";
|
|
@@ -25,61 +25,70 @@ export const SelectedFilters = ({
|
|
|
25
25
|
toggleFilterValue,
|
|
26
26
|
userFilters,
|
|
27
27
|
userFilterScope,
|
|
28
|
-
}) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const options = _.isEqual(filter, selectedFilter)
|
|
49
|
-
? selectedFilterValues
|
|
50
|
-
: null;
|
|
51
|
-
const props = {
|
|
52
|
-
key: filter,
|
|
53
|
-
activeValues,
|
|
54
|
-
closeFilter,
|
|
55
|
-
filter,
|
|
56
|
-
loading,
|
|
57
|
-
openFilter,
|
|
58
|
-
options,
|
|
59
|
-
removeFilter,
|
|
60
|
-
toggleFilterValue,
|
|
61
|
-
};
|
|
62
|
-
return selectedFilter === "taxonomy" ? (
|
|
63
|
-
<FilterMultilevelDropdown {...props} />
|
|
64
|
-
) : (
|
|
65
|
-
<FilterDropdown {...props} />
|
|
66
|
-
);
|
|
67
|
-
})}
|
|
68
|
-
<a className="resetFilters" onClick={() => resetFilters()}>
|
|
69
|
-
<FormattedMessage id="search.clear_filters" />
|
|
70
|
-
</a>
|
|
71
|
-
{saveFilters && (
|
|
72
|
-
<ModalSaveFilter
|
|
73
|
-
saveFilters={saveFilters}
|
|
74
|
-
activeFilters={activeFilters}
|
|
75
|
-
scope={userFilterScope}
|
|
76
|
-
/>
|
|
77
|
-
)}
|
|
78
|
-
</>
|
|
28
|
+
}) => {
|
|
29
|
+
useEffect(
|
|
30
|
+
() => () => {
|
|
31
|
+
resetFilters();
|
|
32
|
+
},
|
|
33
|
+
[resetFilters]
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<>
|
|
38
|
+
{_.isEmpty(userFilters) ? null : (
|
|
39
|
+
<UserFilters
|
|
40
|
+
applyUserFilter={applyUserFilter}
|
|
41
|
+
userFilterScope={userFilterScope}
|
|
42
|
+
deleteUserFilter={deleteUserFilter}
|
|
43
|
+
resetFilters={resetFilters}
|
|
44
|
+
selectedUserFilter={selectedUserFilter}
|
|
45
|
+
userFilters={userFilters}
|
|
46
|
+
disabled={loading}
|
|
47
|
+
/>
|
|
79
48
|
)}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
49
|
+
<div className="selectedFilters">
|
|
50
|
+
{_.isEmpty(selectedFilters) ? null : (
|
|
51
|
+
<>
|
|
52
|
+
<div className="appliedFilters">
|
|
53
|
+
<FormattedMessage id="search.applied_filters" />
|
|
54
|
+
</div>
|
|
55
|
+
{selectedFilters.map((filter) => {
|
|
56
|
+
const options = _.isEqual(filter, selectedFilter)
|
|
57
|
+
? selectedFilterValues
|
|
58
|
+
: null;
|
|
59
|
+
const props = {
|
|
60
|
+
key: filter,
|
|
61
|
+
activeValues,
|
|
62
|
+
closeFilter,
|
|
63
|
+
filter,
|
|
64
|
+
loading,
|
|
65
|
+
openFilter,
|
|
66
|
+
options,
|
|
67
|
+
removeFilter,
|
|
68
|
+
toggleFilterValue,
|
|
69
|
+
};
|
|
70
|
+
return selectedFilter === "taxonomy" ? (
|
|
71
|
+
<FilterMultilevelDropdown {...props} />
|
|
72
|
+
) : (
|
|
73
|
+
<FilterDropdown {...props} />
|
|
74
|
+
);
|
|
75
|
+
})}
|
|
76
|
+
<a className="resetFilters" onClick={() => resetFilters()}>
|
|
77
|
+
<FormattedMessage id="search.clear_filters" />
|
|
78
|
+
</a>
|
|
79
|
+
{saveFilters && (
|
|
80
|
+
<ModalSaveFilter
|
|
81
|
+
saveFilters={saveFilters}
|
|
82
|
+
activeFilters={activeFilters}
|
|
83
|
+
scope={userFilterScope}
|
|
84
|
+
/>
|
|
85
|
+
)}
|
|
86
|
+
</>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
83
92
|
|
|
84
93
|
SelectedFilters.propTypes = {
|
|
85
94
|
activeFilters: PropTypes.object,
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { waitFor, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import { Card } from "semantic-ui-react";
|
|
5
|
+
import { CardGroupsAccordion } from "..";
|
|
6
|
+
|
|
7
|
+
const groups_all_cards_grouped = {
|
|
8
|
+
foo: [
|
|
9
|
+
{
|
|
10
|
+
id: 1,
|
|
11
|
+
name: "hello foo",
|
|
12
|
+
ueueue: "world foo",
|
|
13
|
+
description: "Test description foo 1",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: 2,
|
|
17
|
+
name: "hello foo 2",
|
|
18
|
+
meta: "world foo 2",
|
|
19
|
+
description: "Test description foo 2",
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
bar: [
|
|
23
|
+
{
|
|
24
|
+
id: 3,
|
|
25
|
+
name: "hello bar",
|
|
26
|
+
meta: "world bar",
|
|
27
|
+
description: "Test description bar 1",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 4,
|
|
31
|
+
name: "hello bar 2",
|
|
32
|
+
meta: "world bar 2",
|
|
33
|
+
description: "Test description bar 2",
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const groups_any_cards_without_group = {
|
|
39
|
+
foo: [
|
|
40
|
+
{
|
|
41
|
+
id: 1,
|
|
42
|
+
name: "hello foo",
|
|
43
|
+
meta: "world foo",
|
|
44
|
+
description: "Test description foo 1",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 2,
|
|
48
|
+
name: "hello foo 2",
|
|
49
|
+
meta: "world foo 2",
|
|
50
|
+
description: "Test description foo 2",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
default: [
|
|
54
|
+
{
|
|
55
|
+
id: 3,
|
|
56
|
+
name: "hello bar",
|
|
57
|
+
meta: "world bar",
|
|
58
|
+
description: "Test description bar 1",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 4,
|
|
62
|
+
name: "hello bar 2",
|
|
63
|
+
meta: "world bar 2",
|
|
64
|
+
description: "Test description bar 2",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const cardComponent = (props) => (
|
|
70
|
+
<Card>
|
|
71
|
+
<Card.Content>
|
|
72
|
+
<Card.Header>{props.name}</Card.Header>
|
|
73
|
+
<Card.Meta>
|
|
74
|
+
<span>{props.meta}</span>
|
|
75
|
+
</Card.Meta>
|
|
76
|
+
<Card.Description>{props.description}</Card.Description>
|
|
77
|
+
</Card.Content>
|
|
78
|
+
</Card>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
describe("<CardGroupsAccordion />", () => {
|
|
82
|
+
it("matches the latest snapshot", () => {
|
|
83
|
+
const { container } = render(
|
|
84
|
+
<CardGroupsAccordion
|
|
85
|
+
groups={groups_all_cards_grouped}
|
|
86
|
+
cardComponent={cardComponent}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
expect(container).toMatchSnapshot();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("Data without group appear in without group section", async () => {
|
|
93
|
+
const { getByText } = render(
|
|
94
|
+
<CardGroupsAccordion
|
|
95
|
+
groups={groups_any_cards_without_group}
|
|
96
|
+
cardComponent={cardComponent}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await waitFor(() => {
|
|
101
|
+
expect(getByText("Without Groups")).toBeTruthy();
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("Data without group appear in first position", async () => {
|
|
106
|
+
const { container } = render(
|
|
107
|
+
<CardGroupsAccordion
|
|
108
|
+
groups={groups_any_cards_without_group}
|
|
109
|
+
cardComponent={cardComponent}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const firstGroup = container.getElementsByClassName("title")[0].innerHTML;
|
|
114
|
+
|
|
115
|
+
await waitFor(() => {
|
|
116
|
+
expect(firstGroup).toMatch("Without Groups");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("CardGroupsAccordion fold and unfold when click in title", async () => {
|
|
121
|
+
const { container } = render(
|
|
122
|
+
<CardGroupsAccordion
|
|
123
|
+
groups={groups_any_cards_without_group}
|
|
124
|
+
cardComponent={cardComponent}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const firstTitle = container.querySelector(".title");
|
|
129
|
+
|
|
130
|
+
fireEvent.click(firstTitle);
|
|
131
|
+
|
|
132
|
+
await waitFor(() => {
|
|
133
|
+
expect(firstTitle.classList.contains("active")).toBeFalsy();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
fireEvent.click(firstTitle);
|
|
137
|
+
|
|
138
|
+
await waitFor(() => {
|
|
139
|
+
expect(firstTitle.classList.contains("active")).toBeTruthy();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<CardGroupsAccordion /> matches the latest snapshot 1`] = `
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
class="accordion ui"
|
|
7
|
+
>
|
|
8
|
+
<div>
|
|
9
|
+
<div
|
|
10
|
+
class="active title CardGroupTitle"
|
|
11
|
+
>
|
|
12
|
+
<h3
|
|
13
|
+
class="ui dividing header"
|
|
14
|
+
>
|
|
15
|
+
<i
|
|
16
|
+
aria-hidden="true"
|
|
17
|
+
class="dropdown icon"
|
|
18
|
+
/>
|
|
19
|
+
bar
|
|
20
|
+
</h3>
|
|
21
|
+
</div>
|
|
22
|
+
<div
|
|
23
|
+
class="content active"
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
class="ui cards"
|
|
27
|
+
>
|
|
28
|
+
<div
|
|
29
|
+
class="ui card"
|
|
30
|
+
>
|
|
31
|
+
<div
|
|
32
|
+
class="content"
|
|
33
|
+
>
|
|
34
|
+
<div
|
|
35
|
+
class="header"
|
|
36
|
+
>
|
|
37
|
+
hello bar
|
|
38
|
+
</div>
|
|
39
|
+
<div
|
|
40
|
+
class="meta"
|
|
41
|
+
>
|
|
42
|
+
<span>
|
|
43
|
+
world bar
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
<div
|
|
47
|
+
class="description"
|
|
48
|
+
>
|
|
49
|
+
Test description bar 1
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
<div
|
|
54
|
+
class="ui card"
|
|
55
|
+
>
|
|
56
|
+
<div
|
|
57
|
+
class="content"
|
|
58
|
+
>
|
|
59
|
+
<div
|
|
60
|
+
class="header"
|
|
61
|
+
>
|
|
62
|
+
hello bar 2
|
|
63
|
+
</div>
|
|
64
|
+
<div
|
|
65
|
+
class="meta"
|
|
66
|
+
>
|
|
67
|
+
<span>
|
|
68
|
+
world bar 2
|
|
69
|
+
</span>
|
|
70
|
+
</div>
|
|
71
|
+
<div
|
|
72
|
+
class="description"
|
|
73
|
+
>
|
|
74
|
+
Test description bar 2
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div>
|
|
82
|
+
<div
|
|
83
|
+
class="active title CardGroupTitle"
|
|
84
|
+
>
|
|
85
|
+
<h3
|
|
86
|
+
class="ui dividing header"
|
|
87
|
+
>
|
|
88
|
+
<i
|
|
89
|
+
aria-hidden="true"
|
|
90
|
+
class="dropdown icon"
|
|
91
|
+
/>
|
|
92
|
+
foo
|
|
93
|
+
</h3>
|
|
94
|
+
</div>
|
|
95
|
+
<div
|
|
96
|
+
class="content active"
|
|
97
|
+
>
|
|
98
|
+
<div
|
|
99
|
+
class="ui cards"
|
|
100
|
+
>
|
|
101
|
+
<div
|
|
102
|
+
class="ui card"
|
|
103
|
+
>
|
|
104
|
+
<div
|
|
105
|
+
class="content"
|
|
106
|
+
>
|
|
107
|
+
<div
|
|
108
|
+
class="header"
|
|
109
|
+
>
|
|
110
|
+
hello foo
|
|
111
|
+
</div>
|
|
112
|
+
<div
|
|
113
|
+
class="meta"
|
|
114
|
+
>
|
|
115
|
+
<span />
|
|
116
|
+
</div>
|
|
117
|
+
<div
|
|
118
|
+
class="description"
|
|
119
|
+
>
|
|
120
|
+
Test description foo 1
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<div
|
|
125
|
+
class="ui card"
|
|
126
|
+
>
|
|
127
|
+
<div
|
|
128
|
+
class="content"
|
|
129
|
+
>
|
|
130
|
+
<div
|
|
131
|
+
class="header"
|
|
132
|
+
>
|
|
133
|
+
hello foo 2
|
|
134
|
+
</div>
|
|
135
|
+
<div
|
|
136
|
+
class="meta"
|
|
137
|
+
>
|
|
138
|
+
<span>
|
|
139
|
+
world foo 2
|
|
140
|
+
</span>
|
|
141
|
+
</div>
|
|
142
|
+
<div
|
|
143
|
+
class="description"
|
|
144
|
+
>
|
|
145
|
+
Test description foo 2
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
`;
|
|
@@ -13,6 +13,7 @@ exports[`<OptionGroup /> matches the latest snapshot 1`] = `
|
|
|
13
13
|
minCharacters={1}
|
|
14
14
|
noResultsMessage="No results found."
|
|
15
15
|
onBlur={[Function]}
|
|
16
|
+
onChange={[Function]}
|
|
16
17
|
onClick={[Function]}
|
|
17
18
|
onSearchChange={[Function]}
|
|
18
19
|
open={false}
|
|
@@ -25,8 +26,6 @@ exports[`<OptionGroup /> matches the latest snapshot 1`] = `
|
|
|
25
26
|
selectOnBlur={true}
|
|
26
27
|
selectOnNavigation={true}
|
|
27
28
|
wrapSelection={true}
|
|
28
|
-
|
|
29
|
-
<DropdownMenu />
|
|
30
|
-
</Dropdown>
|
|
29
|
+
/>
|
|
31
30
|
</Fragment>
|
|
32
31
|
`;
|
package/src/components/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import MembersMenu from "./MembersMenu";
|
|
|
4
4
|
import Alert from "./Alert";
|
|
5
5
|
import Authorized from "./Authorized";
|
|
6
6
|
import AvailableFilters from "./AvailableFilters";
|
|
7
|
+
import CardGroupsAccordion from "./CardGroupsAccordion";
|
|
7
8
|
import CatalogMenu from "./CatalogMenu";
|
|
8
9
|
import Comments from "./Comments";
|
|
9
10
|
import CommentsLoader from "./CommentsLoader";
|
|
@@ -51,6 +52,7 @@ export {
|
|
|
51
52
|
Alert,
|
|
52
53
|
Authorized,
|
|
53
54
|
AvailableFilters,
|
|
55
|
+
CardGroupsAccordion,
|
|
54
56
|
CatalogMenu,
|
|
55
57
|
Comments,
|
|
56
58
|
CommentsLoader,
|
package/src/messages/en.js
CHANGED
|
@@ -33,6 +33,7 @@ export default {
|
|
|
33
33
|
"alert.status.504.header": "Gateway Timeout (504)",
|
|
34
34
|
"comments.actions.new": "New comment",
|
|
35
35
|
"comments.header": "Comments",
|
|
36
|
+
"components.CardGroupsAccordion.withoutGroups": "Without Groups",
|
|
36
37
|
"confirmation.no": "No",
|
|
37
38
|
"confirmation.yes": "Yes",
|
|
38
39
|
"dateFilter.since": "less than",
|
package/src/messages/es.js
CHANGED
|
@@ -34,6 +34,7 @@ export default {
|
|
|
34
34
|
"alert.status.504.header": "Gateway Timeout (504)",
|
|
35
35
|
"comments.actions.new": "Añadir comentario",
|
|
36
36
|
"comments.header": "Comentarios",
|
|
37
|
+
"components.CardGroupsAccordion.withoutGroups": "Sin Agrupación",
|
|
37
38
|
"confirmation.no": "No",
|
|
38
39
|
"confirmation.yes": "Sí",
|
|
39
40
|
"dateFilter.since": "hace menos de",
|
package/src/routes.js
CHANGED
|
@@ -36,7 +36,7 @@ export const DOMAINS = "/domains";
|
|
|
36
36
|
export const DOMAINS_ACTIONS = "/domains/:action";
|
|
37
37
|
export const DOMAINS_NEW = "/domains/new";
|
|
38
38
|
export const DOMAINS_SEARCH = "/domains/search";
|
|
39
|
-
export const
|
|
39
|
+
export const DOMAIN_CONCEPTS = "/domains/:id/concepts";
|
|
40
40
|
export const DOMAIN_EDIT = "/domains/:id/edit";
|
|
41
41
|
export const DOMAIN_MEMBERS = "/domains/:id/members";
|
|
42
42
|
export const DOMAIN_MEMBERS_NEW = "/domains/:id/members/new";
|
|
@@ -217,7 +217,7 @@ const routes = {
|
|
|
217
217
|
DOMAINS_ACTIONS,
|
|
218
218
|
DOMAINS_NEW,
|
|
219
219
|
DOMAINS_SEARCH,
|
|
220
|
-
|
|
220
|
+
DOMAIN_CONCEPTS,
|
|
221
221
|
DOMAIN_EDIT,
|
|
222
222
|
DOMAIN_MEMBERS,
|
|
223
223
|
DOMAIN_MEMBERS_NEW,
|
|
@@ -2,12 +2,12 @@ import _ from "lodash/fp";
|
|
|
2
2
|
import {
|
|
3
3
|
createSelector,
|
|
4
4
|
createSelectorCreator,
|
|
5
|
-
defaultMemoize
|
|
5
|
+
defaultMemoize,
|
|
6
6
|
} from "reselect";
|
|
7
7
|
|
|
8
8
|
const defaultFilters = false;
|
|
9
9
|
|
|
10
|
-
export const makeActiveFiltersSelector = activeFiltersProp => {
|
|
10
|
+
export const makeActiveFiltersSelector = (activeFiltersProp) => {
|
|
11
11
|
const activeFiltersSelector = createSelector(
|
|
12
12
|
_.prop(activeFiltersProp),
|
|
13
13
|
(_state, props) => _.propOr(defaultFilters, "defaultFilters")(props),
|
|
@@ -5,7 +5,7 @@ describe("services: filters", () => {
|
|
|
5
5
|
describe("mergeFilters", () => {
|
|
6
6
|
const defaultFilters = {
|
|
7
7
|
status: ["draft", "pending", "rejected"],
|
|
8
|
-
current: [true]
|
|
8
|
+
current: [true],
|
|
9
9
|
};
|
|
10
10
|
const userFilters1 = { type: ["type1"] };
|
|
11
11
|
const userFilters2 = { status: ["draft", "rejected"] };
|
|
@@ -16,7 +16,7 @@ describe("services: filters", () => {
|
|
|
16
16
|
expect(merged).toEqual({
|
|
17
17
|
current: [true],
|
|
18
18
|
status: ["draft", "pending", "rejected"],
|
|
19
|
-
type: ["type1"]
|
|
19
|
+
type: ["type1"],
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -24,7 +24,7 @@ describe("services: filters", () => {
|
|
|
24
24
|
const merged = mergeFilters([defaultFilters, userFilters2]);
|
|
25
25
|
expect(merged).toEqual({
|
|
26
26
|
current: [true],
|
|
27
|
-
status: ["draft", "rejected"]
|
|
27
|
+
status: ["draft", "rejected"],
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
30
|
|
|
@@ -32,9 +32,17 @@ describe("services: filters", () => {
|
|
|
32
32
|
const merged = mergeFilters([defaultFilters, userFilters3]);
|
|
33
33
|
expect(merged).toEqual({
|
|
34
34
|
current: [true],
|
|
35
|
-
status: ["draft", "pending", "rejected"]
|
|
35
|
+
status: ["draft", "pending", "rejected"],
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
it("should use overrides if present for taxonomy key", () => {
|
|
40
|
+
const defaults = { taxonomy: [123] };
|
|
41
|
+
const overrides = { taxonomy: [456] };
|
|
42
|
+
const emptyOverrides = { taxonomy: [] };
|
|
43
|
+
expect(mergeFilters([defaults, overrides])).toEqual(overrides);
|
|
44
|
+
expect(mergeFilters([defaults, emptyOverrides])).toEqual(defaults);
|
|
45
|
+
});
|
|
38
46
|
});
|
|
39
47
|
|
|
40
48
|
describe("getSearchPayload", () => {
|
|
@@ -52,7 +60,7 @@ describe("services: filters", () => {
|
|
|
52
60
|
getSearchPayload(searchQuery, {
|
|
53
61
|
defaultFilters,
|
|
54
62
|
linkable,
|
|
55
|
-
pageSize: size
|
|
63
|
+
pageSize: size,
|
|
56
64
|
})
|
|
57
65
|
).toEqual({
|
|
58
66
|
filters: { ...filters, ...defaultFilters },
|
|
@@ -60,7 +68,7 @@ describe("services: filters", () => {
|
|
|
60
68
|
page,
|
|
61
69
|
query,
|
|
62
70
|
size,
|
|
63
|
-
sort
|
|
71
|
+
sort,
|
|
64
72
|
});
|
|
65
73
|
});
|
|
66
74
|
});
|
package/src/services/filters.js
CHANGED
|
@@ -6,16 +6,16 @@ const intersectionOrDefault = (defaults, overrides) => {
|
|
|
6
6
|
return _.isEmpty(v) ? defaults : v;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
const isNonEmptyArray = (a) => _.isArray(a) && !_.isEmpty(a);
|
|
10
|
+
|
|
11
|
+
const filterCustomizer = (defaults, overrides, key) =>
|
|
12
|
+
key === "taxonomy" && isNonEmptyArray(overrides)
|
|
13
|
+
? overrides
|
|
14
|
+
: isNonEmptyArray(defaults) && isNonEmptyArray(overrides)
|
|
14
15
|
? intersectionOrDefault(defaults, overrides)
|
|
15
16
|
: undefined;
|
|
16
17
|
|
|
17
|
-
export const mergeFilters = (
|
|
18
|
-
_.mergeAllWith(filterCustomizer)(filters);
|
|
18
|
+
export const mergeFilters = _.mergeAllWith(filterCustomizer);
|
|
19
19
|
|
|
20
20
|
export const getSearchPayload = (searchQuery, props) => {
|
|
21
21
|
const { defaultFilters = {}, linkable, pageSize: size = 20 } = props;
|