@truedat/core 5.18.2 → 5.18.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": "5.18.
|
|
3
|
+
"version": "5.18.3",
|
|
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": "
|
|
120
|
+
"gitHead": "ba00f358149f00dcdabb3e3edd883c54c0bbd43a"
|
|
121
121
|
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
|
-
import React from "react";
|
|
2
|
+
import React, { useState, useEffect } from "react";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
4
|
import { FormattedMessage } from "react-intl";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
Label,
|
|
7
|
+
Icon,
|
|
8
|
+
Dropdown,
|
|
9
|
+
Dimmer,
|
|
10
|
+
Loader,
|
|
11
|
+
Input,
|
|
12
|
+
} from "semantic-ui-react";
|
|
13
|
+
import { accentInsensitivePathOrder, lowerDeburr } from "../services/sort";
|
|
7
14
|
import FilterItem from "./FilterItem";
|
|
8
15
|
|
|
9
16
|
const removePrefix = _.replace(/^.*\./, "");
|
|
@@ -17,55 +24,128 @@ export const FilterDropdown = ({
|
|
|
17
24
|
options,
|
|
18
25
|
removeFilter,
|
|
19
26
|
toggleFilterValue,
|
|
20
|
-
}) =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
27
|
+
}) => {
|
|
28
|
+
const [selected, setSelected] = useState();
|
|
29
|
+
const [query, setQuery] = useState();
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const activeOptions = _.filter((option) =>
|
|
33
|
+
_.includes(option.value)(activeValues)
|
|
34
|
+
)(options);
|
|
35
|
+
|
|
36
|
+
_.flow(
|
|
37
|
+
_.reduce((acc, option) => [...acc, option], []),
|
|
38
|
+
_.uniq,
|
|
39
|
+
setSelected
|
|
40
|
+
)(activeOptions);
|
|
41
|
+
}, [activeValues, options]);
|
|
42
|
+
|
|
43
|
+
const handleSearch = (e, { value }) => {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
setQuery(lowerDeburr(value));
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const toogleSelectedOption = (selectedOption) => {
|
|
49
|
+
const newSelectedOptions = selected.find(
|
|
50
|
+
(s) => s.value === selectedOption.value
|
|
51
|
+
)
|
|
52
|
+
? selected.filter((s) => s.value !== selectedOption.value)
|
|
53
|
+
: [...selected, selectedOption];
|
|
54
|
+
setSelected(newSelectedOptions);
|
|
55
|
+
toggleFilterValue({ filter, value: selectedOption.value });
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const match = (name, query) => _.contains(query)(lowerDeburr(name));
|
|
59
|
+
const filterSearch = (all) =>
|
|
60
|
+
query ? _.filter((option) => match(option.text, query))(all) : all;
|
|
61
|
+
|
|
62
|
+
const filteredOptions = filterSearch(options);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Dropdown
|
|
66
|
+
item
|
|
67
|
+
floating
|
|
68
|
+
scrolling
|
|
69
|
+
icon={false}
|
|
70
|
+
upward={false}
|
|
71
|
+
trigger={
|
|
72
|
+
<Label key={filter}>
|
|
73
|
+
<FormattedMessage
|
|
74
|
+
id={`filters.${filter}`}
|
|
75
|
+
defaultMessage={removePrefix(filter)}
|
|
76
|
+
/>
|
|
77
|
+
<Icon
|
|
78
|
+
name="delete"
|
|
79
|
+
onClick={(e) => {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
e.stopPropagation();
|
|
82
|
+
removeFilter({ filter });
|
|
83
|
+
}}
|
|
84
|
+
/>
|
|
85
|
+
</Label>
|
|
86
|
+
}
|
|
87
|
+
onOpen={() => openFilter({ filter })}
|
|
88
|
+
onClose={() => closeFilter({ filter })}
|
|
89
|
+
open={!_.isEmpty(options)}
|
|
90
|
+
>
|
|
91
|
+
<Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
|
|
92
|
+
<>
|
|
93
|
+
{options && options.length >= 8 && (
|
|
94
|
+
<>
|
|
95
|
+
<Input
|
|
96
|
+
icon="search"
|
|
97
|
+
iconPosition="left"
|
|
98
|
+
className="search"
|
|
99
|
+
onKeyDown={(e) => {
|
|
100
|
+
if (e.key === " ") {
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
}
|
|
103
|
+
}}
|
|
104
|
+
onChange={handleSearch}
|
|
105
|
+
onClick={(e) => {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
e.stopPropagation();
|
|
108
|
+
}}
|
|
109
|
+
/>
|
|
110
|
+
{selected &&
|
|
111
|
+
_.flow(
|
|
112
|
+
_.sortBy(accentInsensitivePathOrder("text")),
|
|
113
|
+
_.map.convert({ cap: false })((option, i) => (
|
|
114
|
+
<FilterItem
|
|
115
|
+
key={i}
|
|
116
|
+
filter={filter}
|
|
117
|
+
option={option}
|
|
118
|
+
toggleFilterValue={toogleSelectedOption}
|
|
119
|
+
active={_.includes(_.prop("value")(option))(activeValues)}
|
|
120
|
+
/>
|
|
121
|
+
))
|
|
122
|
+
)(selected)}
|
|
123
|
+
<Dropdown.Divider />
|
|
124
|
+
</>
|
|
125
|
+
)}
|
|
126
|
+
{filteredOptions &&
|
|
127
|
+
_.flow(
|
|
128
|
+
_.sortBy(accentInsensitivePathOrder("text")),
|
|
129
|
+
_.map.convert({ cap: false })((option, i) => (
|
|
130
|
+
<FilterItem
|
|
131
|
+
key={i}
|
|
132
|
+
filter={filter}
|
|
133
|
+
option={option}
|
|
134
|
+
toggleFilterValue={toggleFilterValue}
|
|
135
|
+
active={_.includes(_.prop("value")(option))(activeValues)}
|
|
136
|
+
/>
|
|
137
|
+
))
|
|
138
|
+
)(filteredOptions)}
|
|
139
|
+
</>
|
|
140
|
+
{loading && (
|
|
141
|
+
<Dimmer active inverted>
|
|
142
|
+
<Loader size="tiny" />
|
|
143
|
+
</Dimmer>
|
|
144
|
+
)}
|
|
145
|
+
</Dimmer.Dimmable>
|
|
146
|
+
</Dropdown>
|
|
147
|
+
);
|
|
148
|
+
};
|
|
69
149
|
|
|
70
150
|
FilterDropdown.propTypes = {
|
|
71
151
|
activeValues: PropTypes.array,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import _ from "lodash/fp";
|
|
2
2
|
import React from "react";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { render } from "@truedat/test/render";
|
|
4
|
+
import { waitFor, within, screen } from "@testing-library/react";
|
|
5
|
+
import userEvent from "@testing-library/user-event";
|
|
6
|
+
|
|
5
7
|
import { FilterDropdown } from "../FilterDropdown";
|
|
6
8
|
|
|
7
9
|
const options = _.map((value) => ({ value, text: value }))([
|
|
@@ -9,42 +11,172 @@ const options = _.map((value) => ({ value, text: value }))([
|
|
|
9
11
|
"value2",
|
|
10
12
|
]);
|
|
11
13
|
|
|
14
|
+
const largeOptions = _.range(0, 8).map((n) => ({
|
|
15
|
+
value: `value${n}`,
|
|
16
|
+
text: `value${n}`,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const props = {
|
|
20
|
+
openFilter: jest.fn(),
|
|
21
|
+
closeFilter: jest.fn(),
|
|
22
|
+
removeFilter: jest.fn(),
|
|
23
|
+
toggleFilterValue: jest.fn(),
|
|
24
|
+
filter: "foo",
|
|
25
|
+
options,
|
|
26
|
+
};
|
|
27
|
+
|
|
12
28
|
describe("<FilterDropdown/>", () => {
|
|
13
29
|
it("matches the latest snapshot", () => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
const { container } = render(<FilterDropdown {...props} />);
|
|
31
|
+
expect(container).toMatchSnapshot();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("render search input if has more than eight options", () => {
|
|
35
|
+
const customProps = {
|
|
36
|
+
...props,
|
|
37
|
+
options: largeOptions,
|
|
38
|
+
};
|
|
39
|
+
const { getByRole } = render(<FilterDropdown {...customProps} />);
|
|
40
|
+
|
|
41
|
+
expect(getByRole("textbox")).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("no render search input if has more than eight options", () => {
|
|
45
|
+
const { queryByRole } = render(<FilterDropdown {...props} />);
|
|
46
|
+
expect(queryByRole("textbox")).toBeNull();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("check option check it correctly", () => {
|
|
50
|
+
const customProps = {
|
|
51
|
+
...props,
|
|
52
|
+
options: largeOptions,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const selectedOption = customProps.options[6].text;
|
|
56
|
+
|
|
57
|
+
const { getByRole, getAllByRole, rerender } = render(
|
|
58
|
+
<FilterDropdown {...customProps} />
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(customProps.toggleFilterValue).toBeCalledTimes(0);
|
|
62
|
+
userEvent.click(
|
|
63
|
+
getByRole("option", { name: new RegExp(selectedOption, "i") })
|
|
64
|
+
);
|
|
65
|
+
expect(customProps.toggleFilterValue).toBeCalledWith({
|
|
66
|
+
filter: props.filter,
|
|
67
|
+
value: selectedOption,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const rerenderProps = {
|
|
71
|
+
...customProps,
|
|
72
|
+
activeValues: [selectedOption],
|
|
73
|
+
};
|
|
74
|
+
rerender(<FilterDropdown {...rerenderProps} />);
|
|
75
|
+
|
|
76
|
+
expect(
|
|
77
|
+
within(getAllByRole("option")[0]).queryByText(selectedOption)
|
|
78
|
+
).not.toBeNull();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("type in filter input filter options", () => {
|
|
82
|
+
const customProps = {
|
|
83
|
+
...props,
|
|
84
|
+
options: largeOptions,
|
|
31
85
|
};
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
expect(
|
|
86
|
+
|
|
87
|
+
const searchText = "3";
|
|
88
|
+
|
|
89
|
+
const { getByRole, queryByRole, getAllByRole } = render(
|
|
90
|
+
<FilterDropdown {...customProps} />
|
|
91
|
+
);
|
|
92
|
+
const input = getByRole("textbox");
|
|
93
|
+
|
|
94
|
+
expect(
|
|
95
|
+
getByRole("option", {
|
|
96
|
+
name: new RegExp(customProps.options[1].text, "i"),
|
|
97
|
+
})
|
|
98
|
+
).toBeInTheDocument();
|
|
99
|
+
|
|
100
|
+
userEvent.type(input, searchText);
|
|
101
|
+
|
|
102
|
+
expect(
|
|
103
|
+
queryByRole("option", {
|
|
104
|
+
name: new RegExp(customProps.options[1].text, "i"),
|
|
105
|
+
})
|
|
106
|
+
).toBeNull();
|
|
107
|
+
|
|
108
|
+
expect(
|
|
109
|
+
within(getByRole("option")).getByText(`value${searchText}`)
|
|
110
|
+
).toBeInTheDocument();
|
|
111
|
+
|
|
112
|
+
expect(getAllByRole("option").length).toBe(1);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("type in filter not hidde selected values", () => {
|
|
116
|
+
const selectedOption = largeOptions[6].text;
|
|
117
|
+
|
|
118
|
+
const customProps = {
|
|
119
|
+
...props,
|
|
120
|
+
options: largeOptions,
|
|
121
|
+
activeValues: [selectedOption],
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const searchText = "3";
|
|
125
|
+
|
|
126
|
+
const { getByRole, queryByRole, getAllByRole } = render(
|
|
127
|
+
<FilterDropdown {...customProps} />
|
|
128
|
+
);
|
|
129
|
+
const input = getByRole("textbox");
|
|
130
|
+
|
|
131
|
+
userEvent.type(input, searchText);
|
|
132
|
+
|
|
133
|
+
expect(
|
|
134
|
+
within(getAllByRole("option")[0]).getByText(selectedOption)
|
|
135
|
+
).toBeInTheDocument();
|
|
136
|
+
|
|
137
|
+
expect(
|
|
138
|
+
within(getAllByRole("option")[1]).getByText(`value${searchText}`)
|
|
139
|
+
).toBeInTheDocument();
|
|
140
|
+
|
|
141
|
+
expect(
|
|
142
|
+
queryByRole("option", {
|
|
143
|
+
name: new RegExp(customProps.options[1].text, "i"),
|
|
144
|
+
})
|
|
145
|
+
).toBeNull();
|
|
146
|
+
|
|
147
|
+
expect(getAllByRole("option").length).toBe(2);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("dispatches openFilter", () => {
|
|
151
|
+
const customProps = {
|
|
152
|
+
...props,
|
|
153
|
+
options: [],
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const { rerender } = render(<FilterDropdown {...customProps} />);
|
|
157
|
+
|
|
158
|
+
rerender(<FilterDropdown {...props} />);
|
|
159
|
+
|
|
160
|
+
waitFor(() => expect(props.openFilter).toBeCalledTimes(1));
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("dispatches closeFilter", () => {
|
|
164
|
+
const customProps = {
|
|
165
|
+
...props,
|
|
166
|
+
options: [],
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const { rerender } = render(<FilterDropdown {...props} />);
|
|
170
|
+
rerender(<FilterDropdown {...customProps} />);
|
|
171
|
+
|
|
172
|
+
waitFor(() => expect(props.closeFilter).toBeCalledTimes(1));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("remove filter dispatches removeFilter", async () => {
|
|
176
|
+
const { container } = render(<FilterDropdown {...props} />);
|
|
177
|
+
userEvent.click(container.querySelector('[class="delete icon"]'));
|
|
178
|
+
await waitFor(() => {
|
|
179
|
+
expect(props.removeFilter).toBeCalledTimes(1);
|
|
180
|
+
});
|
|
49
181
|
});
|
|
50
182
|
});
|
|
@@ -1,67 +1,48 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`<FilterDropdown/> matches the latest snapshot 1`] = `
|
|
4
|
-
<
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
floating={true}
|
|
11
|
-
icon={false}
|
|
12
|
-
item={true}
|
|
13
|
-
minCharacters={1}
|
|
14
|
-
noResultsMessage="No results found."
|
|
15
|
-
onClose={[Function]}
|
|
16
|
-
onOpen={[Function]}
|
|
17
|
-
open={true}
|
|
18
|
-
openOnFocus={true}
|
|
19
|
-
renderLabel={[Function]}
|
|
20
|
-
scrolling={true}
|
|
21
|
-
searchInput="text"
|
|
22
|
-
selectOnBlur={true}
|
|
23
|
-
selectOnNavigation={true}
|
|
24
|
-
trigger={
|
|
25
|
-
<Label>
|
|
26
|
-
<Memo(MemoizedFormattedMessage)
|
|
27
|
-
defaultMessage="foo"
|
|
28
|
-
id="filters.foo"
|
|
29
|
-
/>
|
|
30
|
-
<Icon
|
|
31
|
-
as="i"
|
|
32
|
-
name="delete"
|
|
33
|
-
onClick={[Function]}
|
|
34
|
-
/>
|
|
35
|
-
</Label>
|
|
36
|
-
}
|
|
37
|
-
upward={false}
|
|
38
|
-
wrapSelection={true}
|
|
39
|
-
>
|
|
40
|
-
<DimmerDimmable
|
|
41
|
-
as={[Function]}
|
|
4
|
+
<div>
|
|
5
|
+
<div
|
|
6
|
+
aria-expanded="true"
|
|
7
|
+
class="ui active visible floating item scrolling dropdown"
|
|
8
|
+
role="listbox"
|
|
9
|
+
tabindex="0"
|
|
42
10
|
>
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
11
|
+
<div
|
|
12
|
+
class="ui label"
|
|
13
|
+
>
|
|
14
|
+
foo
|
|
15
|
+
<i
|
|
16
|
+
aria-hidden="true"
|
|
17
|
+
class="delete icon"
|
|
18
|
+
/>
|
|
19
|
+
</div>
|
|
20
|
+
<div
|
|
21
|
+
class="menu transition dimmable visible"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
aria-checked="false"
|
|
25
|
+
class="item"
|
|
26
|
+
role="option"
|
|
27
|
+
>
|
|
28
|
+
<i
|
|
29
|
+
aria-hidden="true"
|
|
30
|
+
class="square outline icon"
|
|
31
|
+
/>
|
|
32
|
+
value1
|
|
33
|
+
</div>
|
|
34
|
+
<div
|
|
35
|
+
aria-checked="false"
|
|
36
|
+
class="item"
|
|
37
|
+
role="option"
|
|
38
|
+
>
|
|
39
|
+
<i
|
|
40
|
+
aria-hidden="true"
|
|
41
|
+
class="square outline icon"
|
|
42
|
+
/>
|
|
43
|
+
value2
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
67
48
|
`;
|