@truedat/core 5.17.0 → 5.17.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.17.0",
3
+ "version": "5.17.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": "5fbcca7a428b2b92de1c610eb44f2d4a281c1655"
120
+ "gitHead": "1120a6efa4dfec423696957c5e72d752c9db284f"
121
121
  }
@@ -1,5 +1,5 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
2
+ import React, { Fragment } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Dropdown } from "semantic-ui-react";
5
5
  import { FormattedMessage, useIntl } from "react-intl";
@@ -7,14 +7,41 @@ import { i18nOrder } from "../services/sort";
7
7
 
8
8
  const removePrefix = _.replace(/^.*\./, "");
9
9
 
10
+ const makeFiltersGroup = (filters, groups) =>
11
+ _.groupBy((filter) =>
12
+ _.flow(
13
+ _.find(([_group, fields]) => _.contains(filter)(fields)),
14
+ _.prop("[0]")
15
+ )(groups)
16
+ )(filters);
17
+
10
18
  export const AvailableFilters = ({
11
19
  addFilter,
12
20
  disabled,
13
21
  filters,
14
22
  loading,
15
23
  resetFilters,
24
+ filtersGroup = [],
16
25
  }) => {
17
26
  const { formatMessage } = useIntl();
27
+ const filtersByGroup = makeFiltersGroup(filters, filtersGroup);
28
+
29
+ const groupWithoutFilters = (groups) =>
30
+ _.flow(
31
+ _.prop("[0]"),
32
+ (groupName) => _.prop(groupName)(filtersByGroup),
33
+ _.isEmpty
34
+ )(groups);
35
+
36
+ const groups = _.flow(
37
+ (groups) => _.concat(groups, [[undefined, []]]),
38
+ _.reject(groupWithoutFilters),
39
+ _.map(([groupName, _filters]) => [
40
+ groupName,
41
+ _.prop(groupName)(filtersByGroup),
42
+ ])
43
+ )(filtersGroup);
44
+
18
45
  return (
19
46
  <Dropdown
20
47
  button
@@ -37,20 +64,36 @@ export const AvailableFilters = ({
37
64
  />
38
65
  </em>
39
66
  </Dropdown.Item>
40
- {_.flow(
41
- _.defaultTo([]),
42
- _.sortBy(i18nOrder(formatMessage, "filters")),
43
- _.map((filter) => (
44
- <Dropdown.Item
45
- key={filter}
46
- text={formatMessage({
47
- id: `filters.${filter}`,
48
- defaultMessage: removePrefix(filter),
49
- })}
50
- onClick={() => addFilter({ filter })}
51
- />
52
- ))
53
- )(filters)}
67
+ {_.map.convert({ cap: false })(([groupName, filters], idx) => (
68
+ <Fragment key={idx}>
69
+ {idx == 0 ? null : <Dropdown.Divider />}
70
+ {groupName ? (
71
+ <Dropdown.Header key={groupName}>
72
+ <b>
73
+ <FormattedMessage
74
+ id={`filters.group.header.${groupName}`}
75
+ defaultMessage={groupName}
76
+ />
77
+ </b>
78
+ </Dropdown.Header>
79
+ ) : null}
80
+
81
+ {_.flow(
82
+ _.defaultTo([]),
83
+ _.sortBy(i18nOrder(formatMessage, "filters")),
84
+ _.map((filter) => (
85
+ <Dropdown.Item
86
+ key={filter}
87
+ text={formatMessage({
88
+ id: `filters.${filter}`,
89
+ defaultMessage: removePrefix(filter),
90
+ })}
91
+ onClick={() => addFilter({ filter })}
92
+ />
93
+ ))
94
+ )(filters)}
95
+ </Fragment>
96
+ ))(groups)}
54
97
  </Dropdown.Menu>
55
98
  </Dropdown>
56
99
  );
@@ -62,6 +105,7 @@ AvailableFilters.propTypes = {
62
105
  filters: PropTypes.array,
63
106
  resetFilters: PropTypes.func,
64
107
  loading: PropTypes.bool,
108
+ filtersGroup: PropTypes.array,
65
109
  };
66
110
 
67
111
  export default AvailableFilters;
@@ -1,45 +1,59 @@
1
1
  import _ from "lodash/fp";
2
2
  import React from "react";
3
- import { shallow } from "enzyme";
4
- import { Dropdown } from "semantic-ui-react";
5
- import { intl } from "@truedat/test/intl-stub";
3
+ import { render } from "@truedat/test/render";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { waitFor } from "@testing-library/react";
6
6
  import { AvailableFilters } from "../AvailableFilters";
7
7
 
8
- // workaround for enzyme issue with React.useContext
9
- // see https://github.com/airbnb/enzyme/issues/2176#issuecomment-532361526
10
- jest.spyOn(React, "useContext").mockImplementation(() => intl);
11
-
12
8
  describe("<AvailableFilters/>", () => {
9
+ const resetFilters = jest.fn();
10
+ const addFilter = jest.fn();
11
+
13
12
  const getProps = () => ({
14
- addFilter: jest.fn(),
15
- resetFilters: jest.fn(),
16
- filters: ["foo"],
13
+ addFilter,
14
+ resetFilters,
15
+ filters: ["foo", "bar", "xyz"],
17
16
  });
18
17
 
19
18
  it("matches the latest snapshot", () => {
20
19
  const props = getProps();
21
- const wrapper = shallow(<AvailableFilters {...props} />);
22
- expect(wrapper).toMatchSnapshot();
20
+ const { container } = render(<AvailableFilters {...props} />);
21
+ expect(container).toMatchSnapshot();
23
22
  });
24
23
 
25
- it("dispatches resetFilters when first item is clicked", () => {
24
+ it("dispatches resetFilters when first item is clicked", async () => {
26
25
  const props = getProps();
27
- const wrapper = shallow(<AvailableFilters {...props} />);
26
+ const { queryByText } = render(<AvailableFilters {...props} />);
28
27
 
29
- expect(props.resetFilters.mock.calls.length).toBe(0);
30
- const items = wrapper.find(Dropdown.Item);
31
- expect(items.length).toBe(1 + _.size(props.filters));
32
- items.first().simulate("click");
33
- expect(props.resetFilters.mock.calls.length).toBe(1);
28
+ expect(queryByText(/reset all filters/i)).toBeInTheDocument();
29
+ expect(resetFilters.mock.calls.length).toBe(0);
30
+ userEvent.click(await queryByText(/reset all filters/i));
31
+ await waitFor(() => expect(resetFilters.mock.calls.length).toBe(1));
34
32
  });
35
33
 
36
- it("dispatches addFilter when last item is clicked", () => {
34
+ it("dispatches addFilter when last item is clicked", async () => {
37
35
  const props = getProps();
38
- const wrapper = shallow(<AvailableFilters {...props} />);
36
+ const { queryByText } = render(<AvailableFilters {...props} />);
39
37
 
40
38
  expect(props.addFilter.mock.calls.length).toBe(0);
41
- const items = wrapper.find(Dropdown.Item);
42
- items.last().simulate("click");
39
+ userEvent.click(await queryByText(/foo/i));
43
40
  expect(props.addFilter.mock.calls.length).toBe(1);
44
41
  });
42
+
43
+ it("group by filters", async () => {
44
+ const filtersGroup = [
45
+ ["important", ["xyz"]],
46
+ ["grupNan", ["inexist_filter"]],
47
+ ];
48
+
49
+ const props = {
50
+ addFilter,
51
+ resetFilters,
52
+ filters: ["foo", "bar", "xyz"],
53
+ filtersGroup,
54
+ };
55
+ const { queryByText } = render(<AvailableFilters {...props} />);
56
+ expect(queryByText(/important/i)).toBeInTheDocument();
57
+ expect(queryByText(/grupNan/i)).not.toBeInTheDocument();
58
+ });
45
59
  });
@@ -1,45 +1,67 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
3
  exports[`<AvailableFilters/> matches the latest snapshot 1`] = `
4
- <Dropdown
5
- additionLabel="Add "
6
- additionPosition="top"
7
- button={true}
8
- className="icon"
9
- closeOnBlur={true}
10
- closeOnEscape={true}
11
- deburr={false}
12
- floating={true}
13
- icon="filter"
14
- labeled={true}
15
- minCharacters={1}
16
- noResultsMessage="No results found."
17
- openOnFocus={true}
18
- renderLabel={[Function]}
19
- scrolling={true}
20
- searchInput="text"
21
- selectOnBlur={true}
22
- selectOnNavigation={true}
23
- text="filters"
24
- upward={false}
25
- wrapSelection={true}
26
- >
27
- <DropdownMenu>
28
- <DropdownItem
29
- onClick={[MockFunction]}
4
+ <div>
5
+ <div
6
+ aria-expanded="false"
7
+ class="ui button floating labeled scrolling dropdown icon"
8
+ role="listbox"
9
+ tabindex="0"
10
+ >
11
+ <div
12
+ aria-atomic="true"
13
+ aria-live="polite"
14
+ class="divider text"
15
+ role="alert"
30
16
  >
31
- <em>
32
- <MemoizedFormattedMessage
33
- defaultMessage="(reset filters)"
34
- id="filters.reset"
35
- />
36
- </em>
37
- </DropdownItem>
38
- <DropdownItem
39
- key="foo"
40
- onClick={[Function]}
41
- text="foo"
17
+ Filters
18
+ </div>
19
+ <i
20
+ aria-hidden="true"
21
+ class="filter icon"
42
22
  />
43
- </DropdownMenu>
44
- </Dropdown>
23
+ <div
24
+ class="menu transition"
25
+ >
26
+ <div
27
+ class="item"
28
+ role="option"
29
+ >
30
+ <em>
31
+ (reset all filters)
32
+ </em>
33
+ </div>
34
+ <div
35
+ class="item"
36
+ role="option"
37
+ >
38
+ <span
39
+ class="text"
40
+ >
41
+ bar
42
+ </span>
43
+ </div>
44
+ <div
45
+ class="item"
46
+ role="option"
47
+ >
48
+ <span
49
+ class="text"
50
+ >
51
+ foo
52
+ </span>
53
+ </div>
54
+ <div
55
+ class="item"
56
+ role="option"
57
+ >
58
+ <span
59
+ class="text"
60
+ >
61
+ xyz
62
+ </span>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
45
67
  `;