@truedat/core 6.10.0 → 6.10.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": "6.10.0",
3
+ "version": "6.10.2",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -118,5 +118,5 @@
118
118
  "react-dom": ">= 16.8.6 < 17",
119
119
  "semantic-ui-react": ">= 2.0.3 < 2.2"
120
120
  },
121
- "gitHead": "803604f0c5267ba435ea3e897873fe2b6f10d624"
121
+ "gitHead": "817d6538b10d561ae1de4a342bcb9836f07d8026"
122
122
  }
@@ -1,6 +1,6 @@
1
1
  import _ from "lodash/fp";
2
2
  import { compile } from "path-to-regexp";
3
- import useSWR from "swr";
3
+ import useSWR, { mutate } from "swr";
4
4
  import useSWRMutations from "swr/mutation";
5
5
  import { accentInsensitivePathOrder } from "@truedat/core/services/sort";
6
6
  import {
@@ -15,11 +15,18 @@ import {
15
15
  API_GET_USER_FILTERS,
16
16
  } from "../api";
17
17
 
18
- export const useUserFilters = (type, scope) => {
19
- const url = _.flow(
18
+ const buildUrl = (type, scope) =>
19
+ _.flow(
20
20
  () => compile(API_GET_USER_FILTERS)({ type }),
21
21
  (baseUrl) => `${baseUrl}${scope ? `?scope=${scope}` : ""}`
22
22
  )("");
23
+
24
+ export const mutateUserFilters = (type, scope) => {
25
+ const url = buildUrl(type, scope);
26
+ mutate(url);
27
+ };
28
+ export const useUserFilters = (type, scope) => {
29
+ const url = buildUrl(type, scope);
23
30
  const { data, error, mutate } = useSWR(url, apiJson);
24
31
  const userFilters = data?.data?.data;
25
32
  _.sortBy(accentInsensitivePathOrder("name"))(userFilters);
@@ -0,0 +1,114 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Form } from "semantic-ui-react";
5
+ import { FormattedMessage } from "react-intl";
6
+ import { useAuthorized } from "@truedat/core/hooks";
7
+ import {
8
+ useUserFiltersCreate,
9
+ mutateUserFilters,
10
+ } from "../hooks/useUserFilters";
11
+ import { ConfirmModal } from "../components/ConfirmModal";
12
+ import { useSearchContext } from "./SearchContext";
13
+
14
+ export const SaveFilterForm = ({ setFilterName }) => {
15
+ const onChange = (value) => {
16
+ setFilterName(value);
17
+ };
18
+
19
+ return (
20
+ <Form>
21
+ <Form.Field>
22
+ <label>
23
+ <FormattedMessage id="search.saveFilterForm.filter_name" />
24
+ </label>
25
+ <Form.Input
26
+ name="filter_name"
27
+ onChange={(e, { value }) => onChange(value)}
28
+ />
29
+ </Form.Field>
30
+ </Form>
31
+ );
32
+ };
33
+
34
+ SaveFilterForm.propTypes = {
35
+ setFilterName: PropTypes.func,
36
+ };
37
+
38
+ export const ModalSaveFilter = () => {
39
+ const [filterName, setFilterName] = useState("");
40
+ const authorized = useAuthorized();
41
+
42
+ const {
43
+ allActiveFilters: activeFilters,
44
+ userFiltersType,
45
+ userFilterScope,
46
+ } = useSearchContext();
47
+
48
+ const { trigger: saveFilters } = useUserFiltersCreate(userFiltersType);
49
+
50
+ const handleSubmitGlobal = (e) => doSubmit(e, true);
51
+ const handleSubmit = (e) => doSubmit(e, false);
52
+
53
+ const doSubmit = (e, isGlobal) => {
54
+ e.preventDefault();
55
+ const requestData = {
56
+ user_search_filter: {
57
+ name: filterName,
58
+ filters: activeFilters,
59
+ is_global: isGlobal,
60
+ scope: userFilterScope,
61
+ },
62
+ };
63
+ saveFilters(requestData).then(() => {
64
+ mutateUserFilters(userFiltersType);
65
+ });
66
+ };
67
+
68
+ const actions = [
69
+ {
70
+ key: "no",
71
+ secondary: true,
72
+ content: <FormattedMessage id="search.save_filter.cancel" />,
73
+ },
74
+ {
75
+ key: "yes",
76
+ primary: true,
77
+ disabled: _.isEmpty(filterName),
78
+ content: <FormattedMessage id="search.save_filter.save" />,
79
+ onClick: handleSubmit,
80
+ },
81
+ ...(authorized
82
+ ? [
83
+ {
84
+ key: "yes_global",
85
+ primary: true,
86
+ disabled: _.isEmpty(filterName),
87
+ content: <FormattedMessage id="search.save_filter.save_global" />,
88
+ onClick: handleSubmitGlobal,
89
+ },
90
+ ]
91
+ : []),
92
+ ];
93
+
94
+ return (
95
+ <ConfirmModal
96
+ actions={actions}
97
+ icon="save"
98
+ trigger={
99
+ <a className="resetFilters">
100
+ <FormattedMessage id="search.save_filters" />
101
+ </a>
102
+ }
103
+ header={
104
+ <FormattedMessage id="search.filters.actions.save.confirmation.header" />
105
+ }
106
+ content={
107
+ <SaveFilterForm setFilterName={setFilterName} filterName={filterName} />
108
+ }
109
+ onConfirm={handleSubmit}
110
+ />
111
+ );
112
+ };
113
+
114
+ export default ModalSaveFilter;
@@ -0,0 +1,29 @@
1
+ import React from "react";
2
+ import { Pagination as SemanticPagination } from "semantic-ui-react";
3
+ import { useSearchContext } from "./SearchContext";
4
+
5
+ export const MAX_PAGES = 100;
6
+
7
+ export const Pagination = () => {
8
+ const { count, size, page: activePage, selectPage } = useSearchContext();
9
+ const totalPages = Math.ceil(count / size);
10
+
11
+ return totalPages > MAX_PAGES ? (
12
+ <SemanticPagination
13
+ activePage={activePage}
14
+ boundaryRange={0}
15
+ lastItem={null}
16
+ totalPages={MAX_PAGES}
17
+ onPageChange={(_e, { activePage }) => selectPage({ activePage })}
18
+ />
19
+ ) : totalPages ? (
20
+ <SemanticPagination
21
+ activePage={activePage}
22
+ disabled={totalPages <= 1}
23
+ totalPages={totalPages}
24
+ onPageChange={(_e, { activePage }) => selectPage({ activePage })}
25
+ />
26
+ ) : null;
27
+ };
28
+
29
+ export default Pagination;
@@ -17,7 +17,8 @@ import { makeOption } from "@truedat/core/services/i18n";
17
17
  const SearchContext = createContext();
18
18
 
19
19
  export const SearchContextProvider = (props) => {
20
- const inicialDefaultFilters = _.prop("defaultFilters")(props);
20
+ const initialDefaultFilters = _.prop("defaultFilters")(props);
21
+ const initialActiveFilters = _.propOr({}, "defaultSelectedFilters")(props);
21
22
  const children = _.prop("children")(props);
22
23
  const initialSortColumn = _.prop("initialSortColumn")(props);
23
24
  const initialSortDirection = _.prop("initialSortDirection")(props);
@@ -40,29 +41,36 @@ export const SearchContextProvider = (props) => {
40
41
  const [loadingFilters, setLoadingFilters] = useState(true);
41
42
  const [query, setQuery] = useState("");
42
43
  const [activeFilterName, setActiveFilterName] = useState([]);
43
- const [allActiveFilters, setAllActiveFilters] = useState({});
44
+ const [allActiveFilters, setAllActiveFilters] =
45
+ useState(initialActiveFilters);
44
46
  const [hiddenFilters, setHiddenFilters] = useState({});
45
47
  const [dateFilters, setDateFilters] = useState({});
46
48
  const [toggleDateFilter, setToggleDateFilter] = useState(false);
47
- const [defaultFilters, setDefaultFilters] = useState(inicialDefaultFilters);
49
+ const [defaultFilters, setDefaultFilters] = useState(initialDefaultFilters);
48
50
  const [filterParams, setFilterParams] = useState({});
49
51
  const [sortColumn, setSortColumn] = useState(initialSortColumn);
50
52
  const [sortDirection, setSortDirection] = useState(initialSortDirection);
51
53
  const [page, setPage] = useState(1);
52
54
  const [size, setSize] = useState(pageSize);
53
55
  const [count, setCount] = useState(0);
56
+
57
+ const [onSearchChange, setOnSearchChange] = useState();
58
+
54
59
  useEffect(() => {
55
- if (inicialDefaultFilters !== defaultFilters) {
56
- setDefaultFilters(inicialDefaultFilters);
60
+ if (initialDefaultFilters !== defaultFilters) {
61
+ setDefaultFilters(initialDefaultFilters);
57
62
  }
58
- }, [inicialDefaultFilters]);
63
+ }, [initialDefaultFilters]);
59
64
 
60
65
  //STATE FUNCTIONS
61
66
  const addFilter = ({ filter }) => {
62
67
  setAllActiveFilters({ ...allActiveFilters, [filter]: [] });
63
68
  setActiveFilterName(filter);
64
69
  };
65
- const resetFilters = () => setAllActiveFilters({});
70
+ const resetFilters = () => {
71
+ setAllActiveFilters({});
72
+ onSearchChange && onSearchChange();
73
+ };
66
74
 
67
75
  const openFilter = ({ filter }) => setActiveFilterName(filter);
68
76
  const closeFilter = () => {
@@ -72,6 +80,7 @@ export const SearchContextProvider = (props) => {
72
80
  const removeFilter = ({ filter }) => {
73
81
  setAllActiveFilters(_.omit(filter)(allActiveFilters));
74
82
  setActiveFilterName(activeFilterName == filter ? null : activeFilterName);
83
+ onSearchChange && onSearchChange();
75
84
  };
76
85
 
77
86
  const toggleFilterValue = ({ filter, value }) => {
@@ -83,6 +92,7 @@ export const SearchContextProvider = (props) => {
83
92
  : _.union([value])(values);
84
93
 
85
94
  setAllActiveFilters({ ...allActiveFilters, [filter]: newValue });
95
+ onSearchChange && onSearchChange();
86
96
  };
87
97
 
88
98
  const toggleHiddenFilterValue = ({ filter, value }) => {
@@ -98,9 +108,7 @@ export const SearchContextProvider = (props) => {
98
108
 
99
109
  //CALCULATIONS ON STATE
100
110
 
101
- const selectPage = (props) => {
102
- setPage(props.activePage);
103
- };
111
+ const selectPage = ({ activePage }) => setPage(activePage);
104
112
 
105
113
  const setCountData = (headers) => {
106
114
  setCount(parseInt(_.propOr("0", "x-total-count")(headers)));
@@ -144,21 +152,23 @@ export const SearchContextProvider = (props) => {
144
152
  () => ({
145
153
  ...defaultFilters,
146
154
  ...dateFilters,
155
+ ...hiddenFilters,
147
156
  ..._.pickBy(_.negate(_.isEmpty))(allActiveFilters),
148
157
  }),
149
- [allActiveFilters, defaultFilters, dateFilters]
158
+ [allActiveFilters, defaultFilters, dateFilters, hiddenFilters]
150
159
  );
151
160
 
152
161
  const filterMust = useMemo(
153
162
  () => ({
154
163
  ...defaultFilters,
164
+ ...hiddenFilters,
155
165
  ..._.flow(
156
166
  _.pickBy(_.negate(_.isEmpty)),
157
167
  _.omit(activeFilterName)
158
168
  )(allActiveFilters),
159
169
  }),
160
170
 
161
- [allActiveFilters, activeFilterName, defaultFilters]
171
+ [allActiveFilters, activeFilterName, defaultFilters, hiddenFilters]
162
172
  );
163
173
 
164
174
  const sort = useMemo(
@@ -264,7 +274,10 @@ export const SearchContextProvider = (props) => {
264
274
  setSortColumn(query == "" ? initialSortColumn : "_score");
265
275
  setSortDirection(query == "" ? initialSortDirection : "descending");
266
276
  setQuery(query);
277
+ onSearchChange && onSearchChange();
267
278
  };
279
+
280
+ const cleanQuery = () => setQuery("");
268
281
  const context = {
269
282
  disabled: false,
270
283
  loadingFilters,
@@ -289,8 +302,10 @@ export const SearchContextProvider = (props) => {
289
302
  removeFilter,
290
303
  toggleFilterValue,
291
304
  toggleHiddenFilterValue,
305
+ setHiddenFilters,
292
306
  searchMust,
293
307
  setQuery: setQueryAndScoreSort,
308
+ cleanQuery,
294
309
  setAllActiveFilters,
295
310
  allActiveFilters,
296
311
  filters,
@@ -318,6 +333,8 @@ export const SearchContextProvider = (props) => {
318
333
 
319
334
  userFiltersType,
320
335
  userFilterScope,
336
+
337
+ setOnSearchChange,
321
338
  };
322
339
 
323
340
  return (
@@ -1,8 +1,7 @@
1
1
  import _ from "lodash/fp";
2
2
  import React, { useState } from "react";
3
3
  import { FormattedMessage } from "react-intl";
4
- import { useUserFilters, useUserFiltersCreate } from "../hooks/useUserFilters";
5
- import ModalSaveFilter from "../components/ModalSaveFilter";
4
+ import ModalSaveFilter from "./ModalSaveFilter";
6
5
  import UserFilters from "./UserFilters";
7
6
  import FilterDropdown from "./FilterDropdown";
8
7
  import FilterQueryDropdown from "./FilterQueryDropdown";
@@ -20,38 +19,14 @@ export default function SearchSelectedFilters() {
20
19
  filterTypes,
21
20
  activeFilterName,
22
21
  activeFilterValues,
23
- allActiveFilters,
24
22
  userFiltersType,
25
- userFilterScope,
26
23
  } = context;
27
24
  const [selectedUserFilter, setSelectedUserFilter] = useState();
28
- const { trigger: saveFilters } = useUserFiltersCreate(userFiltersType);
29
- const { userFilters, loading, mutate } = useUserFilters(
30
- userFiltersType,
31
- userFilterScope
32
- );
33
-
34
- const handleSubmit = ({ filterName, filters, isGlobal, scope }) => {
35
- const requestData = {
36
- user_search_filter: {
37
- name: filterName,
38
- filters,
39
- is_global: isGlobal,
40
- scope: scope,
41
- },
42
- };
43
- saveFilters(requestData).then(() => {
44
- mutate();
45
- });
46
- };
47
25
 
48
26
  return (
49
27
  <>
50
- {!_.isEmpty(userFilters) ? (
28
+ {userFiltersType ? (
51
29
  <UserFilters
52
- mutate={mutate}
53
- userFilters={userFilters}
54
- disabled={loading}
55
30
  setSelectedUserFilter={setSelectedUserFilter}
56
31
  selectedUserFilter={selectedUserFilter}
57
32
  />
@@ -94,11 +69,7 @@ export default function SearchSelectedFilters() {
94
69
  >
95
70
  <FormattedMessage id="search.clear_filters" />
96
71
  </a>
97
- <ModalSaveFilter
98
- saveFilters={handleSubmit}
99
- activeFilters={allActiveFilters}
100
- scope={userFilterScope}
101
- />
72
+ {userFiltersType ? <ModalSaveFilter /> : null}
102
73
  </>
103
74
  )}
104
75
  </div>
@@ -1,11 +1,11 @@
1
1
  import _ from "lodash/fp";
2
- import React, { useState } from "react";
2
+ import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { Label, Icon } from "semantic-ui-react";
5
5
  import { FormattedMessage } from "react-intl";
6
6
  import { useAuthorized } from "@truedat/core/hooks";
7
7
  import { useSearchContext } from "@truedat/core/search/SearchContext";
8
- import { useUserFiltersDelete } from "../hooks/useUserFilters";
8
+ import { useUserFilters, useUserFiltersDelete } from "../hooks/useUserFilters";
9
9
  import { ConfirmModal } from "../components/ConfirmModal";
10
10
 
11
11
  export const DeleteModal = ({ userFilter, userFiltersType, mutate }) => {
@@ -57,16 +57,20 @@ DeleteModal.propTypes = {
57
57
  mutate: PropTypes.func,
58
58
  };
59
59
 
60
- export const UserFilters = ({
61
- mutate,
62
- userFilters,
63
- disabled,
64
- setSelectedUserFilter,
65
- selectedUserFilter,
66
- }) => {
60
+ export const UserFilters = ({ setSelectedUserFilter, selectedUserFilter }) => {
67
61
  const authorized = useAuthorized();
68
- const { userFiltersType, resetFilters, setAllActiveFilters } =
69
- useSearchContext();
62
+ const {
63
+ userFiltersType,
64
+ userFilterScope,
65
+ resetFilters,
66
+ setAllActiveFilters,
67
+ } = useSearchContext();
68
+
69
+ const {
70
+ userFilters,
71
+ loading: disabled,
72
+ mutate,
73
+ } = useUserFilters(userFiltersType, userFilterScope);
70
74
 
71
75
  const sortedUserFilters = _.orderBy(
72
76
  ["is_global", "id"],
@@ -124,13 +128,7 @@ export const UserFilters = ({
124
128
 
125
129
  UserFilters.propTypes = {
126
130
  selectedUserFilter: PropTypes.string,
127
- userFilters: PropTypes.array,
128
- disabled: PropTypes.bool,
129
- userFilterScope: PropTypes.string,
130
- mutate: PropTypes.func,
131
- applyUserFilter: PropTypes.func,
132
- deleteUserFilter: PropTypes.func,
133
- resetFilters: PropTypes.func,
131
+ setSelectedUserFilter: PropTypes.func,
134
132
  };
135
133
 
136
134
  export default UserFilters;
@@ -0,0 +1,48 @@
1
+ import React from "react";
2
+ import userEvent from "@testing-library/user-event";
3
+ import { render } from "@truedat/test/render";
4
+ import messages from "@truedat/core/messages";
5
+ import SearchContext from "@truedat/core/search/SearchContext";
6
+ import ModalSaveFilter from "../ModalSaveFilter";
7
+
8
+ const renderOpts = {
9
+ messages: {
10
+ en: {
11
+ ...messages.en,
12
+ },
13
+ },
14
+ };
15
+
16
+ describe("<ModalSaveFilter /> admin", () => {
17
+ jest.mock("../../hooks", () => ({
18
+ ...jest.requireActual("../../hooks"),
19
+ useAuthorized: jest.fn(() => true),
20
+ }));
21
+
22
+ const searchProps = {
23
+ allActiveFilters: {},
24
+ userFiltersType: "filters_type",
25
+ userFilterScope: "filters_scope",
26
+ };
27
+
28
+ it("matches the latest snapshot", () => {
29
+ const { container } = render(
30
+ <SearchContext.Provider value={searchProps}>
31
+ <ModalSaveFilter />
32
+ </SearchContext.Provider>,
33
+ renderOpts
34
+ );
35
+ expect(container).toMatchSnapshot();
36
+ });
37
+
38
+ it("matches the latest snapshot with opened modal", async () => {
39
+ const { container, findByText } = render(
40
+ <SearchContext.Provider value={searchProps}>
41
+ <ModalSaveFilter />
42
+ </SearchContext.Provider>,
43
+ renderOpts
44
+ );
45
+ userEvent.click(await findByText(/save/i));
46
+ expect(container).toMatchSnapshot();
47
+ });
48
+ });
@@ -0,0 +1,21 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ModalSaveFilter /> admin matches the latest snapshot 1`] = `
4
+ <div>
5
+ <a
6
+ class="resetFilters"
7
+ >
8
+ Save
9
+ </a>
10
+ </div>
11
+ `;
12
+
13
+ exports[`<ModalSaveFilter /> admin matches the latest snapshot with opened modal 1`] = `
14
+ <div>
15
+ <a
16
+ class="resetFilters"
17
+ >
18
+ Save
19
+ </a>
20
+ </div>
21
+ `;