@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 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",
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.3",
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": "dfb9df52cce497f8040ba6c17bfb23c9225683d3"
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
- {_.isEmpty(userFilters) ? null : (
31
- <UserFilters
32
- applyUserFilter={applyUserFilter}
33
- userFilterScope={userFilterScope}
34
- deleteUserFilter={deleteUserFilter}
35
- resetFilters={resetFilters}
36
- selectedUserFilter={selectedUserFilter}
37
- userFilters={userFilters}
38
- disabled={loading}
39
- />
40
- )}
41
- <div className="selectedFilters">
42
- {_.isEmpty(selectedFilters) ? null : (
43
- <>
44
- <div className="appliedFilters">
45
- <FormattedMessage id="search.applied_filters" />
46
- </div>
47
- {selectedFilters.map((filter) => {
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
- </div>
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
  `;
@@ -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,
@@ -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",
@@ -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 DOMAIN_ACTION = "/domains/:id/:action";
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
- DOMAIN_ACTION,
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
  });
@@ -6,16 +6,16 @@ const intersectionOrDefault = (defaults, overrides) => {
6
6
  return _.isEmpty(v) ? defaults : v;
7
7
  };
8
8
 
9
- const filterCustomizer = (defaults, overrides) =>
10
- _.isArray(defaults) &&
11
- _.isArray(overrides) &&
12
- !_.isEmpty(defaults) &&
13
- !_.isEmpty(overrides)
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 = (filters) =>
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;
@@ -0,0 +1,3 @@
1
+ .CardGroupTitle {
2
+ margin-top: 1rem;
3
+ }