@truedat/core 8.4.2 → 8.4.3

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": "8.4.2",
3
+ "version": "8.4.3",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -51,7 +51,7 @@
51
51
  "@testing-library/jest-dom": "^6.6.3",
52
52
  "@testing-library/react": "^16.3.0",
53
53
  "@testing-library/user-event": "^14.6.1",
54
- "@truedat/test": "8.4.2",
54
+ "@truedat/test": "8.4.3",
55
55
  "identity-obj-proxy": "^3.0.0",
56
56
  "jest": "^29.7.0",
57
57
  "redux-saga-test-plan": "^4.0.6"
@@ -97,5 +97,5 @@
97
97
  "swr": "^2.3.3",
98
98
  "turndown": "^7.2.2"
99
99
  },
100
- "gitHead": "6ce335c57b6311c56828ce132b816322aa79d9ea"
100
+ "gitHead": "4c27fe8b467a2b0b26f69d4931d85d8bdf674e6d"
101
101
  }
@@ -0,0 +1,213 @@
1
+ import _ from "lodash/fp";
2
+ import { useState, useEffect } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { FormattedMessage } from "react-intl";
5
+ import {
6
+ Label,
7
+ Icon,
8
+ Input,
9
+ Dropdown,
10
+ Dimmer,
11
+ Loader,
12
+ } from "semantic-ui-react";
13
+ import { lowerDeburr } from "../services/sort";
14
+ import DomainSearchFilterItem from "./DomainSearchFilterItem";
15
+
16
+ export const DomainSearchFilter = ({
17
+ name,
18
+ activeValues,
19
+ closeFilter,
20
+ filter,
21
+ loading,
22
+ openFilter,
23
+ options,
24
+ removeFilter,
25
+ toggleFilterValue,
26
+ }) => {
27
+ const [selected, setSelected] = useState();
28
+ const [query, setQuery] = useState();
29
+ const [open, setOpen] = useState([]);
30
+ const [displayed, setDisplayed] = useState([]);
31
+
32
+ useEffect(() => {
33
+ const activeOptions = _.filter((option) =>
34
+ _.includes(option.id)(activeValues)
35
+ )(options);
36
+
37
+ _.flow(_.map("id"), _.uniq, setSelected)(activeOptions);
38
+ if (_.isEmpty(open) && _.isEmpty(displayed)) {
39
+ const withAncestors = _.flow(
40
+ _.reduce(
41
+ (acc, option) => [...acc, ..._.map("id")(option.ancestors)],
42
+ []
43
+ ),
44
+ _.uniq
45
+ )(activeOptions);
46
+ setOpen(withAncestors);
47
+ setDisplayed([..._.map("id")(activeOptions), ...withAncestors]);
48
+ }
49
+ }, [activeValues, options]);
50
+
51
+ const handleOpen = (selection) => {
52
+ const option = _.find({ id: selection })(options);
53
+ const isOpen = _.contains(selection)(open);
54
+ const children = _.map("id")(option.children);
55
+ const descendents = _.map("id")(option.descendents);
56
+
57
+ if (isOpen) {
58
+ setOpen(_.without([selection, ...descendents])(open));
59
+ setDisplayed(_.without(descendents)(displayed));
60
+ } else {
61
+ setOpen(_.union([selection])(open));
62
+ setDisplayed(_.union(children)(displayed));
63
+ }
64
+ };
65
+
66
+ const handleClick = (e, selection) => {
67
+ const option = _.find({ id: selection })(options);
68
+ const descendentIds = _.map("id")(option.descendents);
69
+ const treeIds = [selection, ...descendentIds];
70
+ const value = _.includes(selection)(activeValues)
71
+ ? _.without(treeIds)(activeValues)
72
+ : _.union(treeIds)(activeValues);
73
+ toggleFilterValue({ filter, value });
74
+ };
75
+
76
+ const handleToggleChildren = (_e, selection) => {
77
+ const option = _.find({ id: selection })(options);
78
+ if (!option) return;
79
+ const descendentIds = _.map("id")(option.descendents);
80
+ if (_.isEmpty(descendentIds)) return;
81
+ const allSelected = _.every((id) => _.includes(id)(activeValues))(
82
+ descendentIds
83
+ );
84
+ const value = allSelected
85
+ ? _.without(descendentIds)(activeValues)
86
+ : _.union(descendentIds)(activeValues);
87
+ toggleFilterValue({ filter, value });
88
+ };
89
+
90
+ const displayAll = () => {
91
+ const ids = _.map("id")(options);
92
+ setOpen(ids);
93
+ setDisplayed(ids);
94
+ };
95
+
96
+ const handleSearch = (e, { value }) => {
97
+ e.preventDefault();
98
+ setQuery(lowerDeburr(value));
99
+ if (!_.isEmpty(value)) {
100
+ displayAll();
101
+ }
102
+ };
103
+ const match = (value, currentQuery) =>
104
+ _.contains(currentQuery)(lowerDeburr(value));
105
+ const filterSearch = (all) => {
106
+ if (query) {
107
+ return _.filter(
108
+ (domain) =>
109
+ match(domain.name, query) ||
110
+ _.some((descendent) => match(descendent.name, query))(
111
+ domain.descendents
112
+ )
113
+ )(all);
114
+ }
115
+ return all;
116
+ };
117
+
118
+ const filterDisplayed = (all) =>
119
+ _.filter((domain) => domain.level == 0 || _.contains(domain.id)(displayed))(
120
+ all
121
+ );
122
+
123
+ const filteredOptions = _.flow(filterSearch, filterDisplayed)(options);
124
+
125
+ return (
126
+ <Dropdown
127
+ name={name || "domainSearchFilter"}
128
+ className="domain-search-filter"
129
+ item
130
+ floating
131
+ icon={false}
132
+ upward={false}
133
+ onOpen={() => openFilter({ filter })}
134
+ onClose={() => closeFilter({ filter })}
135
+ trigger={
136
+ <Label key={filter}>
137
+ <FormattedMessage id={`filters.${filter}`} defaultMessage={filter} />
138
+ <Icon
139
+ name="delete"
140
+ onClick={(e) => {
141
+ e.preventDefault();
142
+ e.stopPropagation();
143
+ removeFilter({ filter });
144
+ }}
145
+ />
146
+ </Label>
147
+ }
148
+ open={!_.isEmpty(options)}
149
+ >
150
+ <Dimmer.Dimmable dimmed={loading} as={Dropdown.Menu}>
151
+ <>
152
+ <Input
153
+ icon="search"
154
+ iconPosition="left"
155
+ className="search"
156
+ onKeyDown={(e) => {
157
+ if (e.key === " ") {
158
+ e.stopPropagation();
159
+ }
160
+ }}
161
+ onChange={handleSearch}
162
+ onClick={(e) => {
163
+ e.preventDefault();
164
+ e.stopPropagation();
165
+ }}
166
+ />
167
+ <Dropdown.Menu scrolling>
168
+ {_.map.convert({ cap: false })((option, i) => (
169
+ <DomainSearchFilterItem
170
+ key={i}
171
+ onOpen={handleOpen}
172
+ onClick={handleClick}
173
+ open={_.contains(option.id)(open)}
174
+ canOpen={_.negate(_.isEmpty)(option.children)}
175
+ selected={_.contains(option.id)(selected)}
176
+ onToggleChildren={handleToggleChildren}
177
+ toggleChildrenOn={_.every((id) => _.includes(id)(activeValues))(
178
+ _.map("id")(option.descendents)
179
+ )}
180
+ partialSelected={
181
+ !_.contains(option.id)(selected) &&
182
+ _.some((id) => _.includes(id)(selected))(
183
+ _.map("id")(option.descendents)
184
+ )
185
+ }
186
+ {...option}
187
+ />
188
+ ))(filteredOptions)}
189
+ </Dropdown.Menu>
190
+ </>
191
+ {loading && (
192
+ <Dimmer active inverted>
193
+ <Loader size="tiny" />
194
+ </Dimmer>
195
+ )}
196
+ </Dimmer.Dimmable>
197
+ </Dropdown>
198
+ );
199
+ };
200
+
201
+ DomainSearchFilter.propTypes = {
202
+ name: PropTypes.string,
203
+ activeValues: PropTypes.array,
204
+ closeFilter: PropTypes.func,
205
+ filter: PropTypes.string,
206
+ loading: PropTypes.bool,
207
+ openFilter: PropTypes.func,
208
+ options: PropTypes.array,
209
+ removeFilter: PropTypes.func,
210
+ toggleFilterValue: PropTypes.func,
211
+ };
212
+
213
+ export default DomainSearchFilter;
@@ -0,0 +1,129 @@
1
+ import PropTypes from "prop-types";
2
+ import { useIntl } from "react-intl";
3
+ import { Icon, Dropdown } from "semantic-ui-react";
4
+
5
+ const ToggleChildrenButton = ({ toggleChildrenOn, onClick }) => {
6
+ const { formatMessage } = useIntl();
7
+ const state = toggleChildrenOn ? "on" : "off";
8
+
9
+ const icon = formatMessage({
10
+ id: `domain.selector.toggleChildren.${state}.icon`,
11
+ defaultMessage: toggleChildrenOn ? "toggle on" : "toggle off",
12
+ }).replace("none", "");
13
+
14
+ const label = formatMessage({
15
+ id: `domain.selector.toggleChildren.${state}.label`,
16
+ defaultMessage: "none",
17
+ }).replace("none", "");
18
+
19
+ const hoverText = formatMessage({
20
+ id: `domain.selector.toggleChildren.${state}.hover`,
21
+ defaultMessage: "none",
22
+ }).replace("none", "");
23
+
24
+ if (!icon && !label) return null;
25
+
26
+ return (
27
+ <button type="button" className="toggle-children" onClick={onClick}>
28
+ <span title={hoverText} className="toggle-children-content">
29
+ {icon && <Icon name={icon} />}
30
+ {label && <span>{label}</span>}
31
+ </span>
32
+ </button>
33
+ );
34
+ };
35
+
36
+ ToggleChildrenButton.propTypes = {
37
+ toggleChildrenOn: PropTypes.bool,
38
+ onClick: PropTypes.func,
39
+ };
40
+
41
+ export const DomainSearchFilterItem = ({
42
+ id,
43
+ canOpen,
44
+ onOpen,
45
+ onClick,
46
+ onToggleChildren,
47
+ selected,
48
+ toggleChildrenOn = false,
49
+ partialSelected = false,
50
+ open,
51
+ name,
52
+ level,
53
+ disabled = false,
54
+ className,
55
+ }) => {
56
+ const { formatMessage } = useIntl();
57
+
58
+ const handleOpen = (e) => {
59
+ e && e.preventDefault();
60
+ e && e.stopPropagation();
61
+ onOpen(id);
62
+ };
63
+
64
+ const handleClick = (e) => {
65
+ e && e.preventDefault();
66
+ e && e.stopPropagation();
67
+ onClick(e, id);
68
+ };
69
+
70
+ const handleToggleChildren = (e) => {
71
+ e && e.preventDefault();
72
+ e && e.stopPropagation();
73
+ onToggleChildren && onToggleChildren(e, id);
74
+ };
75
+
76
+ const itemStyle = {
77
+ marginLeft: `${20 * level}px`,
78
+ paddingLeft: !canOpen ? "25px" : "5px",
79
+ };
80
+
81
+ return (
82
+ <Dropdown.Item onClick={handleClick} className={className}>
83
+ <div className="item-content" style={itemStyle}>
84
+ {canOpen ? (
85
+ <Icon
86
+ name={open ? "chevron down" : "chevron right"}
87
+ onClick={handleOpen}
88
+ />
89
+ ) : null}
90
+ <Icon
91
+ name={
92
+ selected
93
+ ? "check square outline"
94
+ : partialSelected
95
+ ? "minus square outline"
96
+ : "square outline"
97
+ }
98
+ />
99
+ <span style={{ opacity: disabled ? 0.45 : 1 }} title={name}>
100
+ {name}
101
+ </span>
102
+ {canOpen && onToggleChildren ? (
103
+ <ToggleChildrenButton
104
+ toggleChildrenOn={toggleChildrenOn}
105
+ onClick={handleToggleChildren}
106
+ />
107
+ ) : null}
108
+ </div>
109
+ </Dropdown.Item>
110
+ );
111
+ };
112
+
113
+ DomainSearchFilterItem.propTypes = {
114
+ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
115
+ canOpen: PropTypes.bool,
116
+ className: PropTypes.string,
117
+ onOpen: PropTypes.func,
118
+ onClick: PropTypes.func,
119
+ onToggleChildren: PropTypes.func,
120
+ selected: PropTypes.bool,
121
+ toggleChildrenOn: PropTypes.bool,
122
+ partialSelected: PropTypes.bool,
123
+ open: PropTypes.bool,
124
+ name: PropTypes.string,
125
+ level: PropTypes.number,
126
+ disabled: PropTypes.bool,
127
+ };
128
+
129
+ export default DomainSearchFilterItem;
@@ -6,6 +6,7 @@ import FilterDropdown from "./FilterDropdown";
6
6
  import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
7
7
  import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
8
8
  import SearchFilterDropdown from "./SearchFilterDropdown";
9
+ import DomainSearchFilter from "./DomainSearchFilter";
9
10
  import ModalSaveFilter from "./ModalSaveFilter";
10
11
  import UserFilters from "./UserFilters";
11
12
 
@@ -30,6 +31,7 @@ export const SelectedFilters = ({
30
31
  userFilterScope,
31
32
  searchFiltersPropsMapping,
32
33
  searchFilterDispacher,
34
+ useDomainSearchFilter,
33
35
  }) => {
34
36
  const { pathname } = useLocation();
35
37
  const taxonomyPathRegex = /^\/domains\/(\d+)\/\w+$/;
@@ -77,7 +79,11 @@ export const SelectedFilters = ({
77
79
 
78
80
  switch (filterType) {
79
81
  case "domain":
80
- return <FilterMultilevelDropdown {...props} />;
82
+ return useDomainSearchFilter ? (
83
+ <DomainSearchFilter {...props} />
84
+ ) : (
85
+ <FilterMultilevelDropdown {...props} />
86
+ );
81
87
  case "hierarchy":
82
88
  return <HierarchyFilterDropdown {...props} />;
83
89
  case "search":
@@ -131,6 +137,7 @@ SelectedFilters.propTypes = {
131
137
  userFilterScope: PropTypes.string,
132
138
  searchFiltersPropsMapping: PropTypes.object,
133
139
  searchFilterDispacher: PropTypes.func,
140
+ useDomainSearchFilter: PropTypes.bool,
134
141
  };
135
142
 
136
143
  export default SelectedFilters;
@@ -0,0 +1,187 @@
1
+ import { fireEvent } from "@testing-library/react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { render } from "@truedat/test/render";
4
+ import { DomainSearchFilter } from "../DomainSearchFilter";
5
+
6
+ jest.mock("../DomainSearchFilterItem", () => {
7
+ const MockDomainSearchFilterItem = ({
8
+ id,
9
+ name,
10
+ onOpen,
11
+ onClick,
12
+ onToggleChildren,
13
+ }) => (
14
+ <div>
15
+ <span>{name}</span>
16
+ <button type="button" onClick={() => onOpen(id)}>
17
+ {`open-${id}`}
18
+ </button>
19
+ <button type="button" onClick={(e) => onClick(e, id)}>
20
+ {`select-${id}`}
21
+ </button>
22
+ <button type="button" onClick={(e) => onToggleChildren(e, id)}>
23
+ {`toggle-${id}`}
24
+ </button>
25
+ </div>
26
+ );
27
+
28
+ return {
29
+ __esModule: true,
30
+ DomainSearchFilterItem: MockDomainSearchFilterItem,
31
+ default: MockDomainSearchFilterItem,
32
+ };
33
+ });
34
+
35
+ describe("<DomainSearchFilter />", () => {
36
+ const getProps = (overrides = {}) => ({
37
+ filter: "taxonomy",
38
+ activeValues: [],
39
+ closeFilter: jest.fn(),
40
+ openFilter: jest.fn(),
41
+ removeFilter: jest.fn(),
42
+ toggleFilterValue: jest.fn(),
43
+ loading: false,
44
+ options: [
45
+ {
46
+ id: 1,
47
+ name: "Domain 1",
48
+ level: 0,
49
+ ancestors: [],
50
+ children: [{ id: 2, name: "Domain 2", level: 1 }],
51
+ descendents: [{ id: 2, name: "Domain 2", level: 1 }],
52
+ },
53
+ {
54
+ id: 2,
55
+ name: "Domain 2",
56
+ level: 1,
57
+ ancestors: [{ id: 1, name: "Domain 1" }],
58
+ children: [],
59
+ descendents: [],
60
+ },
61
+ ],
62
+ ...overrides,
63
+ });
64
+
65
+ it("matches the latest snapshot", () => {
66
+ const rendered = render(<DomainSearchFilter {...getProps()} />);
67
+ expect(rendered.container).toMatchSnapshot();
68
+ });
69
+
70
+ it("calls removeFilter when delete is clicked", async () => {
71
+ const props = getProps();
72
+ const user = userEvent.setup({ delay: null });
73
+ const rendered = render(<DomainSearchFilter {...props} />);
74
+
75
+ await user.click(rendered.container.querySelector(".delete.icon"));
76
+
77
+ expect(props.removeFilter).toHaveBeenCalledWith({ filter: "taxonomy" });
78
+ });
79
+
80
+ it("calls toggleFilterValue adding selected option and descendents", async () => {
81
+ const props = getProps();
82
+ const user = userEvent.setup({ delay: null });
83
+ const rendered = render(<DomainSearchFilter {...props} />);
84
+
85
+ await user.click(rendered.getByText(/select-1/i));
86
+
87
+ expect(props.toggleFilterValue).toHaveBeenCalledWith({
88
+ filter: "taxonomy",
89
+ value: [1, 2],
90
+ });
91
+ });
92
+
93
+ it("calls toggleFilterValue removing selected option and descendents", async () => {
94
+ const props = getProps({ activeValues: [1, 2] });
95
+ const user = userEvent.setup({ delay: null });
96
+ const rendered = render(<DomainSearchFilter {...props} />);
97
+
98
+ await user.click(rendered.getByText(/select-1/i));
99
+
100
+ expect(props.toggleFilterValue).toHaveBeenCalledWith({
101
+ filter: "taxonomy",
102
+ value: [],
103
+ });
104
+ });
105
+
106
+ it("calls toggleFilterValue adding descendents from toggle children", async () => {
107
+ const props = getProps();
108
+ const user = userEvent.setup({ delay: null });
109
+ const rendered = render(<DomainSearchFilter {...props} />);
110
+
111
+ await user.click(rendered.getByText(/toggle-1/i));
112
+
113
+ expect(props.toggleFilterValue).toHaveBeenCalledWith({
114
+ filter: "taxonomy",
115
+ value: [2],
116
+ });
117
+ });
118
+
119
+ it("calls toggleFilterValue removing descendents from toggle children", async () => {
120
+ const props = getProps({ activeValues: [2] });
121
+ const user = userEvent.setup({ delay: null });
122
+ const rendered = render(<DomainSearchFilter {...props} />);
123
+
124
+ await user.click(rendered.getByText(/toggle-1/i));
125
+
126
+ expect(props.toggleFilterValue).toHaveBeenCalledWith({
127
+ filter: "taxonomy",
128
+ value: [],
129
+ });
130
+ });
131
+
132
+ it("shows child domains when searching by descendent name", async () => {
133
+ const props = getProps();
134
+ const user = userEvent.setup({ delay: null });
135
+ const rendered = render(<DomainSearchFilter {...props} />);
136
+ const searchInput = rendered.container.querySelector(".search input");
137
+
138
+ expect(rendered.queryByText(/domain 2/i)).not.toBeInTheDocument();
139
+
140
+ await user.type(searchInput, "domain 2");
141
+
142
+ expect(rendered.getByText(/domain 2/i)).toBeInTheDocument();
143
+ });
144
+
145
+ it("does not expand all domains when search value is empty", () => {
146
+ const props = getProps();
147
+ const rendered = render(<DomainSearchFilter {...props} />);
148
+ const searchInput = rendered.container.querySelector(".search input");
149
+
150
+ fireEvent.change(searchInput, { target: { value: "" } });
151
+
152
+ expect(rendered.queryByText(/domain 2/i)).not.toBeInTheDocument();
153
+ });
154
+
155
+ it("stops propagation on space keydown in search input", async () => {
156
+ const props = getProps();
157
+ const user = userEvent.setup({ delay: null });
158
+ const onKeyDown = jest.fn();
159
+ const rendered = render(
160
+ <div onKeyDown={onKeyDown}>
161
+ <DomainSearchFilter {...props} />
162
+ </div>
163
+ );
164
+ const searchInput = rendered.container.querySelector(".search input");
165
+
166
+ await user.click(searchInput);
167
+ await user.keyboard(" ");
168
+
169
+ expect(onKeyDown).not.toHaveBeenCalled();
170
+ });
171
+
172
+ it("prevents default and stops propagation on search input click", async () => {
173
+ const props = getProps();
174
+ const user = userEvent.setup({ delay: null });
175
+ const onClick = jest.fn();
176
+ const rendered = render(
177
+ <div onClick={onClick}>
178
+ <DomainSearchFilter {...props} />
179
+ </div>
180
+ );
181
+ const searchInput = rendered.container.querySelector(".search input");
182
+
183
+ await user.click(searchInput);
184
+
185
+ expect(onClick).not.toHaveBeenCalled();
186
+ });
187
+ });
@@ -0,0 +1,106 @@
1
+ import userEvent from "@testing-library/user-event";
2
+ import { render } from "@truedat/test/render";
3
+ import { DomainSearchFilterItem } from "../DomainSearchFilterItem";
4
+
5
+ describe("<DomainSearchFilterItem />", () => {
6
+ const getProps = (overrides = {}) => ({
7
+ id: 1,
8
+ canOpen: true,
9
+ onOpen: jest.fn(),
10
+ onClick: jest.fn(),
11
+ onToggleChildren: jest.fn(),
12
+ selected: false,
13
+ toggleChildrenOn: false,
14
+ partialSelected: false,
15
+ open: false,
16
+ name: "Domain 1",
17
+ level: 0,
18
+ disabled: false,
19
+ ...overrides,
20
+ });
21
+
22
+ it("matches the latest snapshot", () => {
23
+ const rendered = render(<DomainSearchFilterItem {...getProps()} />);
24
+ expect(rendered.container).toMatchSnapshot();
25
+ });
26
+
27
+ it("matches snapshot when selected is true", () => {
28
+ const rendered = render(
29
+ <DomainSearchFilterItem {...getProps()} selected={true} />
30
+ );
31
+ expect(rendered.container).toMatchSnapshot();
32
+ });
33
+
34
+ it("matches snapshot when partial selected is true", () => {
35
+ const rendered = render(
36
+ <DomainSearchFilterItem
37
+ {...getProps()}
38
+ selected={false}
39
+ partialSelected={true}
40
+ />
41
+ );
42
+ expect(rendered.container).toMatchSnapshot();
43
+ });
44
+
45
+ it("calls onOpen when chevron icon is clicked", async () => {
46
+ const props = getProps({ canOpen: true, open: false });
47
+ const user = userEvent.setup({ delay: null });
48
+ const rendered = render(<DomainSearchFilterItem {...props} />);
49
+
50
+ await user.click(rendered.container.querySelector(".chevron.right.icon"));
51
+
52
+ expect(props.onOpen).toHaveBeenCalledWith(1);
53
+ });
54
+
55
+ it("calls onClick with event and id when item is clicked", async () => {
56
+ const props = getProps();
57
+ const user = userEvent.setup({ delay: null });
58
+ const rendered = render(<DomainSearchFilterItem {...props} />);
59
+
60
+ await user.click(rendered.getByText(/domain 1/i));
61
+
62
+ expect(props.onClick).toHaveBeenCalledTimes(1);
63
+ expect(props.onClick.mock.calls[0][1]).toBe(1);
64
+ });
65
+
66
+ it("calls onToggleChildren when toggle icon is clicked", async () => {
67
+ const props = getProps({ canOpen: true, toggleChildrenOn: false });
68
+ const user = userEvent.setup({ delay: null });
69
+ const rendered = render(<DomainSearchFilterItem {...props} />);
70
+
71
+ await user.click(rendered.container.querySelector(".toggle-children"));
72
+
73
+ expect(props.onToggleChildren).toHaveBeenCalledTimes(1);
74
+ expect(props.onToggleChildren.mock.calls[0][1]).toBe(1);
75
+ });
76
+
77
+ it("does not render chevron and toggle icons when canOpen is false", () => {
78
+ const rendered = render(
79
+ <DomainSearchFilterItem {...getProps({ canOpen: false })} />
80
+ );
81
+
82
+ expect(
83
+ rendered.container.querySelector(".chevron.right.icon")
84
+ ).not.toBeInTheDocument();
85
+ expect(
86
+ rendered.container.querySelector(".toggle-children")
87
+ ).not.toBeInTheDocument();
88
+ });
89
+
90
+ it("does not render the toggle button when icon and label i18n are none", () => {
91
+ const messages = {
92
+ en: {
93
+ "domain.selector.toggleChildren.off.icon": "none",
94
+ "domain.selector.toggleChildren.off.label": "none",
95
+ },
96
+ };
97
+ const rendered = render(
98
+ <DomainSearchFilterItem {...getProps({ canOpen: true })} />,
99
+ { messages }
100
+ );
101
+
102
+ expect(
103
+ rendered.container.querySelector(".toggle-children")
104
+ ).not.toBeInTheDocument();
105
+ });
106
+ });