@truedat/core 5.18.1 → 5.18.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "5.18.1",
3
+ "version": "5.18.2",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -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": "95d5a42f5bd259ad53fa66d4fe8cce48be73ee03"
120
+ "gitHead": "d69ace0ddcf34baa4f4b60a6862a053327c1d299"
121
121
  }
@@ -60,6 +60,7 @@ export const ConfirmModal = ({
60
60
  <Modal.Content>{content}</Modal.Content>
61
61
  )
62
62
  }
63
+ role="dialog"
63
64
  />
64
65
  );
65
66
 
@@ -14,6 +14,7 @@ import { lowerDeburr } from "../services/sort";
14
14
  import DropdownMenuItem from "./DropdownMenuItem";
15
15
 
16
16
  export const FilterMultilevelDropdown = ({
17
+ name,
17
18
  activeValues,
18
19
  closeFilter,
19
20
  filter,
@@ -130,6 +131,7 @@ export const FilterMultilevelDropdown = ({
130
131
 
131
132
  return (
132
133
  <Dropdown
134
+ name={name || "filterMultilevelDropdown"}
133
135
  item
134
136
  floating
135
137
  icon={false}
@@ -193,6 +195,7 @@ export const FilterMultilevelDropdown = ({
193
195
  };
194
196
 
195
197
  FilterMultilevelDropdown.propTypes = {
198
+ name: PropTypes.string,
196
199
  activeValues: PropTypes.array,
197
200
  closeFilter: PropTypes.func,
198
201
  filter: PropTypes.string,
@@ -72,6 +72,7 @@ const PopulatedHierarchyFilterDropdown = (props) => {
72
72
 
73
73
  return (
74
74
  <FilterMultilevelDropdown
75
+ name="hierarchyFilterDropdown"
75
76
  activeValues={idActiveValues}
76
77
  closeFilter={closeFilter}
77
78
  filter={filter}
@@ -97,7 +98,7 @@ PopulatedHierarchyFilterDropdown.propTypes = {
97
98
 
98
99
  const HierarchyFilterDropdown = (props) =>
99
100
  _.isEmpty(props.options) ? (
100
- <FilterMultilevelDropdown {...props} />
101
+ <FilterMultilevelDropdown {...props} name="hierarchyFilterDropdown" />
101
102
  ) : (
102
103
  <PopulatedHierarchyFilterDropdown {...props} />
103
104
  );
@@ -0,0 +1,155 @@
1
+ import React, { useState } from "react";
2
+ import PropTypes from "prop-types";
3
+
4
+ import _ from "lodash/fp";
5
+ import { useIntl } from "react-intl";
6
+
7
+ import {
8
+ Label,
9
+ Icon,
10
+ Dropdown,
11
+ Dimmer,
12
+ Loader,
13
+ Input,
14
+ } from "semantic-ui-react";
15
+ import { accentInsensitivePathOrder } from "../services/sort";
16
+
17
+ const removePrefix = _.replace(/^.*\./, "");
18
+ const SPACE_CODE = 32;
19
+
20
+ export const SearchFilterDropdown = ({
21
+ query,
22
+ searchCallback,
23
+ placeholder,
24
+ options,
25
+ open,
26
+ loading,
27
+ activeValues,
28
+ closeFilter,
29
+ filter,
30
+ FilterDataLoader,
31
+ loaderProps,
32
+ FilterItem,
33
+ openFilter,
34
+ removeFilter,
35
+ toggleFilterValue,
36
+ searchFilterDispacher,
37
+ }) => {
38
+ const [selectedOptions, setSelectedOptions] = useState([]);
39
+ const { formatMessage } = useIntl();
40
+
41
+ const toogleSelectedOption = (selectedOption) => {
42
+ const newSelectedOptions = selectedOptions.find(
43
+ (s) => s.id === selectedOption.id
44
+ )
45
+ ? selectedOptions.filter((s) => s.id !== selectedOption.id)
46
+ : [...selectedOptions, selectedOption];
47
+ setSelectedOptions(newSelectedOptions);
48
+ toggleFilterValue({ filter, value: selectedOption.id });
49
+ };
50
+
51
+ return (
52
+ <Dropdown
53
+ name="searchFilterDropdown"
54
+ item
55
+ floating
56
+ scrolling
57
+ icon={false}
58
+ upward={false}
59
+ trigger={
60
+ <Label key={filter}>
61
+ {formatMessage({
62
+ id: `filters.${filter}`,
63
+ defaultMessage: removePrefix(filter),
64
+ })}
65
+ <Icon
66
+ name="delete"
67
+ onClick={(e) => {
68
+ e.preventDefault();
69
+ e.stopPropagation();
70
+ removeFilter({ filter });
71
+ }}
72
+ />
73
+ </Label>
74
+ }
75
+ onOpen={() => openFilter({ filter })}
76
+ onClose={(e) => {
77
+ // Next line Fix bug: https://github.com/Semantic-Org/Semantic-UI-React/pull/3766
78
+ if (e?.type === "keydown" && e?.keyCode === SPACE_CODE) return;
79
+ closeFilter({ filter });
80
+ }}
81
+ open={open}
82
+ >
83
+ <Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
84
+ <FilterDataLoader {...loaderProps} />
85
+ <Input
86
+ icon="search"
87
+ iconPosition="left"
88
+ placeholder={placeholder}
89
+ // Next line Fix bug: https://github.com/Semantic-Org/Semantic-UI-React/issues/1593
90
+ onClick={(e) => e.stopPropagation()}
91
+ value={query}
92
+ onChange={(_e, data) =>
93
+ searchFilterDispacher(searchCallback({ query: data.value }))
94
+ }
95
+ // Next line Fix bug: https://github.com/Semantic-Org/Semantic-UI-React/issues/4374
96
+ onKeyDown={(e) => e.keyCode === SPACE_CODE && e.stopPropagation()}
97
+ />
98
+ {selectedOptions &&
99
+ _.flow(
100
+ _.sortBy(accentInsensitivePathOrder("text")),
101
+ _.map.convert({ cap: false })((option, i) => (
102
+ <FilterItem
103
+ key={i}
104
+ filter={filter}
105
+ option={option}
106
+ toggleFilterValue={toogleSelectedOption}
107
+ active={_.includes(_.prop("id")(option))(activeValues)}
108
+ />
109
+ ))
110
+ )(selectedOptions)}
111
+ <Dropdown.Divider />
112
+ {options &&
113
+ _.flow(
114
+ _.sortBy(accentInsensitivePathOrder("text")),
115
+ _.filter((o) => !_.includes(_.prop("id")(o))(activeValues)),
116
+ _.map.convert({ cap: false })((option, i) => (
117
+ <FilterItem
118
+ key={i}
119
+ filter={filter}
120
+ option={option}
121
+ toggleFilterValue={toogleSelectedOption}
122
+ active={_.includes(_.prop("id")(option))(activeValues)}
123
+ />
124
+ ))
125
+ )(options)}
126
+ {loading && (
127
+ <Dimmer active inverted>
128
+ <Loader size="tiny" />
129
+ </Dimmer>
130
+ )}
131
+ </Dimmer.Dimmable>
132
+ </Dropdown>
133
+ );
134
+ };
135
+
136
+ SearchFilterDropdown.propTypes = {
137
+ query: PropTypes.string,
138
+ searchCallback: PropTypes.func,
139
+ placeholder: PropTypes.string,
140
+ options: PropTypes.object,
141
+ open: PropTypes.bool,
142
+ loading: PropTypes.bool,
143
+ activeValues: PropTypes.array,
144
+ closeFilter: PropTypes.func,
145
+ filter: PropTypes.string,
146
+ FilterDataLoader: PropTypes.node,
147
+ loaderProps: PropTypes.object,
148
+ FilterItem: PropTypes.object,
149
+ openFilter: PropTypes.func,
150
+ removeFilter: PropTypes.func,
151
+ toggleFilterValue: PropTypes.func,
152
+ searchFilterDispacher: PropTypes.func,
153
+ };
154
+
155
+ export default SearchFilterDropdown;
@@ -5,6 +5,7 @@ import { FormattedMessage } from "react-intl";
5
5
  import FilterDropdown from "./FilterDropdown";
6
6
  import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
7
7
  import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
8
+ import SearchFilterDropdown from "./SearchFilterDropdown";
8
9
  import ModalSaveFilter from "./ModalSaveFilter";
9
10
  import UserFilters from "./UserFilters";
10
11
 
@@ -27,6 +28,8 @@ export const SelectedFilters = ({
27
28
  toggleFilterValue,
28
29
  userFilters,
29
30
  userFilterScope,
31
+ searchFiltersPropsMapping,
32
+ searchFilterDispacher,
30
33
  }) => (
31
34
  <>
32
35
  {_.isEmpty(userFilters) ? null : (
@@ -47,10 +50,10 @@ export const SelectedFilters = ({
47
50
  <FormattedMessage id="search.applied_filters" />
48
51
  </div>
49
52
  {selectedFilters.map((filter) => {
53
+ const isSelectedFilter = _.isEqual(filter, selectedFilter);
54
+
50
55
  const filterType = _.prop(filter)(filterTypes);
51
- const options = _.isEqual(filter, selectedFilter)
52
- ? selectedFilterValues
53
- : null;
56
+ const options = isSelectedFilter ? selectedFilterValues : null;
54
57
 
55
58
  const props = {
56
59
  key: filter,
@@ -63,13 +66,24 @@ export const SelectedFilters = ({
63
66
  removeFilter,
64
67
  toggleFilterValue,
65
68
  };
66
- return filterType === "domain" ? (
67
- <FilterMultilevelDropdown {...props} />
68
- ) : filterType === "hierarchy" ? (
69
- <HierarchyFilterDropdown {...props} />
70
- ) : (
71
- <FilterDropdown {...props} />
72
- );
69
+
70
+ switch (filterType) {
71
+ case "domain":
72
+ return <FilterMultilevelDropdown {...props} />;
73
+ case "hierarchy":
74
+ return <HierarchyFilterDropdown {...props} />;
75
+ case "search":
76
+ return (
77
+ <SearchFilterDropdown
78
+ {...props}
79
+ {...searchFiltersPropsMapping[filter]}
80
+ open={isSelectedFilter}
81
+ searchFilterDispacher={searchFilterDispacher}
82
+ />
83
+ );
84
+ default:
85
+ return <FilterDropdown {...props} />;
86
+ }
73
87
  })}
74
88
  <a className="resetFilters" onClick={() => resetFilters()}>
75
89
  <FormattedMessage id="search.clear_filters" />
@@ -106,6 +120,8 @@ SelectedFilters.propTypes = {
106
120
  toggleFilterValue: PropTypes.func,
107
121
  userFilters: PropTypes.array,
108
122
  userFilterScope: PropTypes.string,
123
+ searchFiltersPropsMapping: PropTypes.object,
124
+ searchFilterDispacher: PropTypes.func,
109
125
  };
110
126
 
111
127
  export default SelectedFilters;
@@ -0,0 +1,33 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Icon, Dropdown, Segment } from "semantic-ui-react";
5
+
6
+ const preventDefault = (e, callback) => {
7
+ e && e.preventDefault();
8
+ e && e.stopPropagation();
9
+ callback();
10
+ };
11
+
12
+ export const StructureFilterItem = ({ active, toggleFilterValue, option }) => (
13
+ <Dropdown.Item
14
+ onClick={(e) => preventDefault(e, () => toggleFilterValue(option))}
15
+ active={active}
16
+ >
17
+ <Segment vertical>
18
+ <Icon name={active ? "check square outline" : "square outline"} />
19
+ {_.trim(option.name)}
20
+ <small>{` - (${option.type})`}</small>
21
+ <br />
22
+ <small>{option.path.join(" > ")}</small>
23
+ </Segment>
24
+ </Dropdown.Item>
25
+ );
26
+
27
+ StructureFilterItem.propTypes = {
28
+ active: PropTypes.bool,
29
+ toggleFilterValue: PropTypes.func,
30
+ option: PropTypes.object,
31
+ };
32
+
33
+ export default StructureFilterItem;
@@ -4,7 +4,10 @@ import { shallow } from "enzyme";
4
4
  import { Dropdown } from "semantic-ui-react";
5
5
  import { FilterDropdown } from "../FilterDropdown";
6
6
 
7
- const options = _.map(value => ({ value, text: value }))(["value1", "value2"]);
7
+ const options = _.map((value) => ({ value, text: value }))([
8
+ "value1",
9
+ "value2",
10
+ ]);
8
11
 
9
12
  describe("<FilterDropdown/>", () => {
10
13
  it("matches the latest snapshot", () => {
@@ -24,7 +27,7 @@ describe("<FilterDropdown/>", () => {
24
27
  filter,
25
28
  openFilter,
26
29
  options,
27
- removeFilter
30
+ removeFilter,
28
31
  };
29
32
  const wrapper = shallow(<FilterDropdown {...props} />);
30
33
  expect(openFilter.mock.calls.length).toBe(0);
@@ -0,0 +1,106 @@
1
+ /* eslint-disable react/prop-types */
2
+ import React from "react";
3
+ import { render } from "@truedat/test/render";
4
+ import { within, waitFor } from "@testing-library/react";
5
+ import userEvent from "@testing-library/user-event";
6
+
7
+ import { Icon, Dropdown, Segment } from "semantic-ui-react";
8
+
9
+ import SearchFilterDropdown from "../SearchFilterDropdown";
10
+
11
+ const DummyLoader = () => <div></div>;
12
+ const DummyFilerItem = ({ active, toggleFilterValue, option }) => (
13
+ <Dropdown.Item onClick={() => toggleFilterValue(option)} active={active}>
14
+ <Segment vertical>
15
+ <Icon name={active ? "check square outline" : "square outline"} />
16
+ {option.name}
17
+ </Segment>
18
+ </Dropdown.Item>
19
+ );
20
+
21
+ const props = {
22
+ query: "foo_query",
23
+ searchCallback: jest.fn(),
24
+ placeholder: "search dropdown",
25
+ options: [
26
+ {
27
+ id: 1,
28
+ name: "foo_option",
29
+ },
30
+ {
31
+ id: 2,
32
+ name: "bar_option",
33
+ },
34
+ {
35
+ id: 3,
36
+ name: "baz_option",
37
+ },
38
+ ],
39
+ open: true,
40
+ loading: false,
41
+ activeValues: [1],
42
+ closeFilter: jest.fn(),
43
+ filter: "foo_filter",
44
+ FilterDataLoader: DummyLoader,
45
+ loaderProps: { name: "loader-prop" },
46
+ FilterItem: DummyFilerItem,
47
+ openFilter: jest.fn(),
48
+ removeFilter: jest.fn(),
49
+ toggleFilterValue: jest.fn(),
50
+ searchFilterDispacher: jest.fn(),
51
+ };
52
+
53
+ describe("<SearchFilterDropDown/>", () => {
54
+ it("matches the latest snapshot unfolded", () => {
55
+ const { container } = render(<SearchFilterDropdown {...props} />);
56
+ expect(container).toMatchSnapshot();
57
+ });
58
+
59
+ it("matches the latest snapshot folded", () => {
60
+ const customProps = {
61
+ ...props,
62
+ open: false,
63
+ };
64
+ const { container } = render(<SearchFilterDropdown {...customProps} />);
65
+ expect(container).toMatchSnapshot();
66
+ });
67
+
68
+ it("check option check it correctly", () => {
69
+ const { getByRole, getAllByRole } = render(
70
+ <SearchFilterDropdown {...props} />
71
+ );
72
+
73
+ expect(props.toggleFilterValue).toBeCalledTimes(0);
74
+ userEvent.click(getByRole("option", { name: "baz_option" }));
75
+ expect(props.toggleFilterValue).toBeCalledWith({
76
+ value: 3,
77
+ filter: "foo_filter",
78
+ });
79
+
80
+ expect(
81
+ within(getAllByRole("option")[0]).queryByText("baz_option")
82
+ ).not.toBeNull();
83
+ });
84
+
85
+ it("type in filter input dispatch searchCallback with correct query", () => {
86
+ const searchText = "a";
87
+
88
+ const { getByRole } = render(<SearchFilterDropdown {...props} />);
89
+ const input = getByRole("textbox");
90
+
91
+ userEvent.type(input, searchText);
92
+
93
+ expect(props.searchCallback).toBeCalledWith({
94
+ query: `${props.query}${searchText}`,
95
+ });
96
+ expect(props.searchFilterDispacher).toBeCalledTimes(1);
97
+ });
98
+
99
+ it("remove filter dispatches removeFilter", async () => {
100
+ const { container } = render(<SearchFilterDropdown {...props} />);
101
+ userEvent.click(container.querySelector('[class="delete icon"]'));
102
+ await waitFor(() => {
103
+ expect(props.removeFilter).toBeCalledTimes(1);
104
+ });
105
+ });
106
+ });
@@ -1,66 +1,94 @@
1
+ /* eslint-disable react/prop-types */
1
2
  import React from "react";
2
- import { shallowWithIntl } from "@truedat/test/intl-stub";
3
+ import { render } from "@truedat/test/render";
4
+
5
+ import userEvent from "@testing-library/user-event";
6
+ import { Icon, Dropdown, Segment } from "semantic-ui-react";
7
+
3
8
  import { SelectedFilters } from "../SelectedFilters";
4
9
 
10
+ const DummyLoader = () => <div></div>;
11
+ const DummyFilerItem = ({ active, toggleFilterValue, option }) => (
12
+ <Dropdown.Item onClick={() => toggleFilterValue(option)} active={active}>
13
+ <Segment vertical>
14
+ <Icon name={active ? "check square outline" : "square outline"} />
15
+ {option.name}
16
+ </Segment>
17
+ </Dropdown.Item>
18
+ );
19
+
5
20
  describe("<SelectedFilters/>", () => {
6
- const getProps = () => ({
21
+ const props = {
7
22
  resetFilters: jest.fn(),
8
23
  filterTypes: {
9
24
  foo: "fooType",
10
25
  },
26
+ openFilter: jest.fn(),
27
+ closeFilter: jest.fn(),
11
28
  selectedFilter: "foo",
12
29
  selectedFilters: ["foo", "bar"],
13
30
  selectedFilterActiveValues: ["value2"],
14
31
  selectedFilterValues: ["value1", "value2"],
15
- saveFilters: jest.fn(),
16
32
  selectedUserFilter: null,
17
33
  userFilters: ["uf1", "uf2"],
18
- });
34
+ searchFiltersPropsMapping: {},
35
+ searchFilterDispacher: jest.fn(),
36
+ };
37
+
38
+ const messages = {
39
+ "search.clear_filters": "clean filters",
40
+ "search.save_filters": "save filters",
41
+ };
42
+
43
+ const renderOptions = {
44
+ messages: {
45
+ en: messages,
46
+ },
47
+ };
19
48
 
20
49
  it("matches the latest snapshot", () => {
21
- const props = getProps();
22
- const wrapper = shallowWithIntl(<SelectedFilters {...props} />);
23
- expect(wrapper).toMatchSnapshot();
50
+ const { container } = render(<SelectedFilters {...props} />);
51
+ expect(container).toMatchSnapshot();
24
52
  });
25
53
 
26
54
  it("dispatches resetFilters", () => {
27
- const props = getProps();
28
- const wrapper = shallowWithIntl(<SelectedFilters {...props} />);
55
+ const { getByText } = render(<SelectedFilters {...props} />, renderOptions);
29
56
 
30
- expect(props.resetFilters.mock.calls.length).toBe(0);
31
- wrapper.find("a").simulate("click");
32
- expect(props.resetFilters.mock.calls.length).toBe(1);
57
+ expect(props.resetFilters).toBeCalledTimes(0);
58
+ userEvent.click(getByText(messages["search.clear_filters"]));
59
+ expect(props.resetFilters).toBeCalledTimes(1);
33
60
  });
34
61
 
35
62
  it("does not render Save filters option if saveFilters is undefined", () => {
36
- const props = getProps();
37
- const customProps = { ...props, saveFilters: undefined };
38
- const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
39
- expect(wrapper.find("ModalSaveFilter")).toHaveLength(0);
63
+ const { queryByText } = render(
64
+ <SelectedFilters {...props} />,
65
+ renderOptions
66
+ );
67
+ expect(queryByText(messages["search.save_filters"])).toBeNull();
40
68
  });
41
69
 
42
70
  it("renders Save filters option if saveFilters is defined", () => {
43
- const props = getProps();
44
- const wrapper = shallowWithIntl(<SelectedFilters {...props} />);
45
- expect(wrapper.find("ModalSaveFilter")).toHaveLength(1);
71
+ const customProps = { ...props, saveFilters: jest.fn() };
72
+ const { getByText } = render(
73
+ <SelectedFilters {...customProps} />,
74
+ renderOptions
75
+ );
76
+ expect(getByText(messages["search.save_filters"])).toBeInTheDocument();
46
77
  });
47
78
 
48
79
  it("does not render userFilters if they are empty", () => {
49
- const props = getProps();
50
80
  const customProps = { ...props, userFilters: [] };
51
- const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
52
- expect(wrapper.find("UserFilters")).toHaveLength(0);
81
+ const { container } = render(<SelectedFilters {...customProps} />);
82
+ expect(container.getElementsByClassName("userFilter")).toHaveLength(0);
53
83
  });
54
84
 
55
85
  it("renders userFilters if they are defined", () => {
56
- const props = getProps();
57
86
  const customProps = { ...props, userFilters: [{ country: ["a"] }] };
58
- const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
59
- expect(wrapper.find("UserFilters")).toHaveLength(1);
87
+ const { container } = render(<SelectedFilters {...customProps} />);
88
+ expect(container.getElementsByClassName("userFilter")).toHaveLength(1);
60
89
  });
61
90
 
62
91
  it("renders FilterMultilevelDropdown if filter is of type domain", () => {
63
- const props = getProps();
64
92
  const customProps = {
65
93
  ...props,
66
94
  selectedFilters: ["taxonomy"],
@@ -68,20 +96,49 @@ describe("<SelectedFilters/>", () => {
68
96
  filterTypes: { taxonomy: "domain" },
69
97
  selectedFilterValues: [{ id: 1, name: "foo", level: 0 }],
70
98
  };
71
- const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
72
- expect(wrapper.find("FilterMultilevelDropdown")).toHaveLength(1);
99
+ const { container } = render(<SelectedFilters {...customProps} />);
100
+ expect(
101
+ container.querySelector('[name="filterMultilevelDropdown"]')
102
+ ).not.toBeNull();
73
103
  });
74
104
 
75
105
  it("renders HierarchyFilterDropdown if filter is of type hierarchy", () => {
76
- const props = getProps();
77
106
  const customProps = {
78
107
  ...props,
79
108
  selectedFilters: ["hierarchy_field"],
80
109
  selectedFilter: "hierarchy_field",
81
110
  filterTypes: { hierarchy_field: "hierarchy" },
82
- selectedFilterValues: [{ key: "2_1", id: 1, name: "foo", level: 0 }],
111
+ selectedFilterValues: [],
112
+ };
113
+ const { container } = render(<SelectedFilters {...customProps} />);
114
+ expect(
115
+ container.querySelector('[name="hierarchyFilterDropdown"]')
116
+ ).not.toBeNull();
117
+ });
118
+
119
+ it("renders SearchFilterDropdown if filter is of type search", () => {
120
+ const customProps = {
121
+ ...props,
122
+ selectedFilters: ["search_filter"],
123
+ selectedFilter: "search_filter",
124
+ filterTypes: { search_filter: "search" },
125
+ selectedFilterValues: [],
126
+ searchFiltersPropsMapping: {
127
+ search_filter: {
128
+ query: "query test",
129
+ searchCallback: jest.fn(),
130
+ options: [],
131
+ placeholder: "test",
132
+ FilterDataLoader: DummyLoader,
133
+ loaderProps: { pageSize: 50 },
134
+ FilterItem: DummyFilerItem,
135
+ },
136
+ },
137
+ searchFilterDispacher: jest.fn(),
83
138
  };
84
- const wrapper = shallowWithIntl(<SelectedFilters {...customProps} />);
85
- expect(wrapper.find("HierarchyFilterDropdown")).toHaveLength(1);
139
+ const { container } = render(<SelectedFilters {...customProps} />);
140
+ expect(
141
+ container.querySelector('[name="searchFilterDropdown"]')
142
+ ).not.toBeNull();
86
143
  });
87
144
  });
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import { within } from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { StructureFilterItem } from "../StructureFilterItem";
6
+
7
+ const props = {
8
+ active: true,
9
+ filter: "foo",
10
+ toggleFilterValue: jest.fn(),
11
+ option: { id: 123, name: "bar", path: ["one", "two", "three"], type: "baz" },
12
+ };
13
+
14
+ describe("<StructureFilterItem/>", () => {
15
+ it("matches the latest snapshot", () => {
16
+ const { container } = render(<StructureFilterItem {...props} />);
17
+ expect(container).toMatchSnapshot();
18
+ });
19
+
20
+ it("dispatches toggleFilterValue on click", () => {
21
+ const { container } = render(<StructureFilterItem {...props} />);
22
+ expect(props.toggleFilterValue.mock.calls.length).toBe(0);
23
+ userEvent.click(within(container).getByText(props.option.name));
24
+ expect(props.toggleFilterValue.mock.calls.length).toBe(1);
25
+ });
26
+ });
@@ -5,6 +5,7 @@ exports[`<FilterMultilevelDropdown /> matches the latest snapshot 1`] = `
5
5
  <div
6
6
  aria-expanded="true"
7
7
  class="ui active visible floating item dropdown"
8
+ name="filterMultilevelDropdown"
8
9
  role="listbox"
9
10
  tabindex="0"
10
11
  >
@@ -5,6 +5,7 @@ exports[`<HierarchyFilterDropdown /> matches the latest snapshot 1`] = `
5
5
  <div
6
6
  aria-expanded="true"
7
7
  class="ui active visible floating item dropdown"
8
+ name="hierarchyFilterDropdown"
8
9
  role="listbox"
9
10
  tabindex="0"
10
11
  >
@@ -68,6 +69,7 @@ exports[`<HierarchyFilterDropdown /> matches the latest snapshot for empty optio
68
69
  <div
69
70
  aria-expanded="false"
70
71
  class="ui floating item dropdown"
72
+ name="hierarchyFilterDropdown"
71
73
  role="listbox"
72
74
  tabindex="0"
73
75
  >
@@ -0,0 +1,147 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<SearchFilterDropDown/> matches the latest snapshot folded 1`] = `
4
+ <div>
5
+ <div
6
+ aria-expanded="false"
7
+ class="ui floating item scrolling dropdown"
8
+ name="searchFilterDropdown"
9
+ role="listbox"
10
+ tabindex="0"
11
+ >
12
+ <div
13
+ class="ui label"
14
+ >
15
+ foo_filter
16
+ <i
17
+ aria-hidden="true"
18
+ class="delete icon"
19
+ />
20
+ </div>
21
+ <div
22
+ class="menu transition dimmable"
23
+ >
24
+ <div />
25
+ <div
26
+ class="ui left icon input"
27
+ >
28
+ <input
29
+ placeholder="search dropdown"
30
+ type="text"
31
+ value="foo_query"
32
+ />
33
+ <i
34
+ aria-hidden="true"
35
+ class="search icon"
36
+ />
37
+ </div>
38
+ <div
39
+ class="divider"
40
+ />
41
+ <div
42
+ aria-checked="false"
43
+ class="item"
44
+ role="option"
45
+ >
46
+ <div
47
+ class="ui vertical segment"
48
+ >
49
+ <i
50
+ aria-hidden="true"
51
+ class="square outline icon"
52
+ />
53
+ bar_option
54
+ </div>
55
+ </div>
56
+ <div
57
+ aria-checked="false"
58
+ class="item"
59
+ role="option"
60
+ >
61
+ <div
62
+ class="ui vertical segment"
63
+ >
64
+ <i
65
+ aria-hidden="true"
66
+ class="square outline icon"
67
+ />
68
+ baz_option
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ </div>
74
+ `;
75
+
76
+ exports[`<SearchFilterDropDown/> matches the latest snapshot unfolded 1`] = `
77
+ <div>
78
+ <div
79
+ aria-expanded="true"
80
+ class="ui active visible floating item scrolling dropdown"
81
+ name="searchFilterDropdown"
82
+ role="listbox"
83
+ tabindex="0"
84
+ >
85
+ <div
86
+ class="ui label"
87
+ >
88
+ foo_filter
89
+ <i
90
+ aria-hidden="true"
91
+ class="delete icon"
92
+ />
93
+ </div>
94
+ <div
95
+ class="menu transition dimmable visible"
96
+ >
97
+ <div />
98
+ <div
99
+ class="ui left icon input"
100
+ >
101
+ <input
102
+ placeholder="search dropdown"
103
+ type="text"
104
+ value="foo_query"
105
+ />
106
+ <i
107
+ aria-hidden="true"
108
+ class="search icon"
109
+ />
110
+ </div>
111
+ <div
112
+ class="divider"
113
+ />
114
+ <div
115
+ aria-checked="false"
116
+ class="item"
117
+ role="option"
118
+ >
119
+ <div
120
+ class="ui vertical segment"
121
+ >
122
+ <i
123
+ aria-hidden="true"
124
+ class="square outline icon"
125
+ />
126
+ bar_option
127
+ </div>
128
+ </div>
129
+ <div
130
+ aria-checked="false"
131
+ class="item"
132
+ role="option"
133
+ >
134
+ <div
135
+ class="ui vertical segment"
136
+ >
137
+ <i
138
+ aria-hidden="true"
139
+ class="square outline icon"
140
+ />
141
+ baz_option
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ `;
@@ -1,63 +1,111 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<SelectedFilters/> matches the latest snapshot 1`] = `
4
- <Fragment>
5
- <UserFilters
6
- resetFilters={[MockFunction]}
7
- selectedUserFilter={null}
8
- userFilters={
9
- [
10
- "uf1",
11
- "uf2",
12
- ]
13
- }
14
- />
4
+ <div>
15
5
  <div
16
- className="selectedFilters"
6
+ class="selectedFilters"
17
7
  >
18
8
  <div
19
- className="appliedFilters"
9
+ class="ui circular label"
20
10
  >
21
- <MemoizedFormattedMessage
22
- id="search.applied_filters"
11
+ <span
12
+ class="userFilter"
13
+ />
14
+ <i
15
+ aria-hidden="true"
16
+ class="grey trash alternate outline small icon selectable"
23
17
  />
24
18
  </div>
25
- <FilterDropdown
26
- activeValues={
27
- [
28
- "value2",
29
- ]
30
- }
31
- filter="foo"
32
- key="foo"
33
- options={
34
- [
35
- "value1",
36
- "value2",
37
- ]
38
- }
39
- />
40
- <FilterDropdown
41
- activeValues={
42
- [
43
- "value2",
44
- ]
45
- }
46
- filter="bar"
47
- key="bar"
48
- options={null}
49
- />
50
- <a
51
- className="resetFilters"
52
- onClick={[Function]}
19
+ <div
20
+ class="ui circular label"
21
+ >
22
+ <span
23
+ class="userFilter"
24
+ />
25
+ <i
26
+ aria-hidden="true"
27
+ class="grey trash alternate outline small icon selectable"
28
+ />
29
+ </div>
30
+ </div>
31
+ <div
32
+ class="selectedFilters"
33
+ >
34
+ <div
35
+ class="appliedFilters"
36
+ >
37
+ Filters:
38
+ </div>
39
+ <div
40
+ aria-expanded="true"
41
+ class="ui active visible floating item scrolling dropdown"
42
+ role="listbox"
43
+ tabindex="0"
44
+ >
45
+ <div
46
+ class="ui label"
47
+ >
48
+ foo
49
+ <i
50
+ aria-hidden="true"
51
+ class="delete icon"
52
+ />
53
+ </div>
54
+ <div
55
+ class="menu transition dimmable visible"
56
+ >
57
+ <div
58
+ aria-checked="false"
59
+ class="item"
60
+ role="option"
61
+ >
62
+ <i
63
+ aria-hidden="true"
64
+ class="square outline icon"
65
+ />
66
+ <i>
67
+ Empty
68
+ </i>
69
+ </div>
70
+ <div
71
+ aria-checked="false"
72
+ class="item"
73
+ role="option"
74
+ >
75
+ <i
76
+ aria-hidden="true"
77
+ class="square outline icon"
78
+ />
79
+ <i>
80
+ Empty
81
+ </i>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ <div
86
+ aria-expanded="false"
87
+ class="ui floating item scrolling dropdown"
88
+ role="listbox"
89
+ tabindex="0"
53
90
  >
54
- <MemoizedFormattedMessage
55
- id="search.clear_filters"
91
+ <div
92
+ class="ui label"
93
+ >
94
+ bar
95
+ <i
96
+ aria-hidden="true"
97
+ class="delete icon"
98
+ />
99
+ </div>
100
+ <div
101
+ class="menu transition dimmable"
56
102
  />
103
+ </div>
104
+ <a
105
+ class="resetFilters"
106
+ >
107
+ Clear filters
57
108
  </a>
58
- <ModalSaveFilter
59
- saveFilters={[MockFunction]}
60
- />
61
109
  </div>
62
- </Fragment>
110
+ </div>
63
111
  `;
@@ -0,0 +1,28 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<StructureFilterItem/> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ aria-checked="true"
7
+ class="active item"
8
+ role="option"
9
+ >
10
+ <div
11
+ class="ui vertical segment"
12
+ >
13
+ <i
14
+ aria-hidden="true"
15
+ class="check square outline icon"
16
+ />
17
+ bar
18
+ <small>
19
+ - (baz)
20
+ </small>
21
+ <br />
22
+ <small>
23
+ one &gt; two &gt; three
24
+ </small>
25
+ </div>
26
+ </div>
27
+ </div>
28
+ `;
@@ -39,11 +39,13 @@ import RichTextEditor from "./RichTextEditor";
39
39
  import RouteListener from "./RouteListener";
40
40
  import SafeLink from "./SafeLink";
41
41
  import ScrollToTop from "./ScrollToTop";
42
+ import SearchFilterDropdown from "./SearchFilterDropdown";
42
43
  import SearchInput from "./SearchInput";
43
44
  import SearchMenu from "./SearchMenu";
44
45
  import SelectedFilters from "./SelectedFilters";
45
46
  import SidebarToggle from "./SidebarToggle";
46
47
  import SideMenu from "./SideMenu";
48
+ import StructureFilterItem from "./StructureFilterItem";
47
49
  import Submenu from "./Submenu";
48
50
  import TaxonomyMenu from "./TaxonomyMenu";
49
51
  import TemplateSelector from "./TemplateSelector";
@@ -93,11 +95,13 @@ export {
93
95
  RouteListener,
94
96
  SafeLink,
95
97
  ScrollToTop,
98
+ SearchFilterDropdown,
96
99
  SearchInput,
97
100
  SearchMenu,
98
101
  SelectedFilters,
99
102
  SidebarToggle,
100
103
  SideMenu,
104
+ StructureFilterItem,
101
105
  Submenu,
102
106
  TaxonomyMenu,
103
107
  TemplateSelector,