@truedat/dd 7.6.2 → 7.6.4

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.
Files changed (38) hide show
  1. package/package.json +3 -3
  2. package/src/api.js +2 -2
  3. package/src/components/GrantRoutes.js +2 -16
  4. package/src/components/Grants.js +43 -16
  5. package/src/components/GrantsDownloadButton.js +34 -23
  6. package/src/components/GrantsLabelResults.js +8 -14
  7. package/src/components/GrantsPagination.js +12 -10
  8. package/src/components/GrantsTable.js +23 -27
  9. package/src/components/__tests__/GrantsDownloadButton.spec.js +150 -9
  10. package/src/components/__tests__/GrantsLabelResults.spec.js +12 -1
  11. package/src/components/__tests__/GrantsTable.spec.js +31 -10
  12. package/src/components/__tests__/__snapshots__/GrantRoutes.spec.js.snap +0 -18
  13. package/src/components/__tests__/__snapshots__/GrantsDownloadButton.spec.js.snap +1 -3
  14. package/src/components/__tests__/__snapshots__/GrantsTable.spec.js.snap +0 -37
  15. package/src/components/index.js +0 -2
  16. package/src/hooks/useGrant.js +49 -2
  17. package/src/messages/en.js +1 -1
  18. package/src/messages/es.js +1 -1
  19. package/src/reducers/index.js +0 -2
  20. package/src/routines.js +0 -3
  21. package/src/sagas/index.js +0 -3
  22. package/src/selectors/getGrantsColumns.js +6 -12
  23. package/src/selectors/index.js +0 -4
  24. package/src/components/GrantDateFilter.js +0 -27
  25. package/src/components/GrantSelectedFilters.js +0 -59
  26. package/src/components/GrantsSearch.js +0 -50
  27. package/src/components/__tests__/GrantDateFilter.spec.js +0 -18
  28. package/src/components/__tests__/__snapshots__/GrantDateFilter.spec.js.snap +0 -34
  29. package/src/reducers/__tests__/grantDateFilter.spec.js +0 -54
  30. package/src/reducers/grantDateFilter.js +0 -30
  31. package/src/sagas/__tests__/downloadGrants.spec.js +0 -85
  32. package/src/sagas/downloadGrants.js +0 -46
  33. package/src/selectors/__tests__/getGrantSelectedFilterActiveValues.spec.js +0 -16
  34. package/src/selectors/__tests__/getGrantSelectedFilterValues.spec.js +0 -15
  35. package/src/selectors/getGrantFilterTypes.js +0 -7
  36. package/src/selectors/getGrantSelectedFilterActiveValues.js +0 -8
  37. package/src/selectors/getGrantSelectedFilterValues.js +0 -12
  38. package/src/selectors/getPreviousGrantQuery.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "7.6.2",
3
+ "version": "7.6.4",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "module": "src/index.js",
@@ -48,7 +48,7 @@
48
48
  "@testing-library/jest-dom": "^6.6.3",
49
49
  "@testing-library/react": "^16.3.0",
50
50
  "@testing-library/user-event": "^14.6.1",
51
- "@truedat/test": "7.6.2",
51
+ "@truedat/test": "7.6.4",
52
52
  "identity-obj-proxy": "^3.0.0",
53
53
  "jest": "^29.7.0",
54
54
  "redux-saga-test-plan": "^4.0.6"
@@ -83,5 +83,5 @@
83
83
  "svg-pan-zoom": "^3.6.2",
84
84
  "swr": "^2.3.3"
85
85
  },
86
- "gitHead": "413392c768bdc4e352be8863a70b031483b9ca88"
86
+ "gitHead": "994343683c2e0fb3fab85a021e627b1b86a7831f"
87
87
  }
package/src/api.js CHANGED
@@ -22,7 +22,7 @@ const API_GRANT_REQUEST_BULK_APPROVAL = "/api/grant_requests/bulk_approval";
22
22
  const API_GRANT_REQUESTS_SEARCH = "/api/grant_requests/search";
23
23
  const API_GRANT_REQUESTS_FILTERS_SEARCH = "/api/grant_requests_filters/search";
24
24
  const API_GRANT = "/api/grants/:id";
25
- const API_GRANTS_CSV = "/api/grants/csv";
25
+ const API_GRANTS_DOWNLOAD = "/api/grants/xlsx/download";
26
26
  const API_GRANTS_SEARCH = "/api/grants/search";
27
27
  const API_GRAPH = "/api/graphs/:id";
28
28
  const API_GRAPH_HASH = "/api/graphs/hash/:hash";
@@ -83,7 +83,7 @@ export {
83
83
  API_GRAPH_HASH,
84
84
  API_GRAPHS,
85
85
  API_GRAPH_CSV,
86
- API_GRANTS_CSV,
86
+ API_GRANTS_DOWNLOAD,
87
87
  API_LINEAGE_EVENTS,
88
88
  API_MY_GRANTS_SEARCH,
89
89
  API_MY_GRANT_FILTERS_SEARCH,
@@ -33,14 +33,12 @@ import GrantApprovalRule from "./GrantApprovalRule";
33
33
  import GrantApprovalRuleEdit from "./GrantApprovalRuleEdit";
34
34
  import GrantApprovalRuleNew from "./GrantApprovalRuleNew";
35
35
  import GrantApprovalRules from "./GrantApprovalRules";
36
- import GrantFiltersLoader from "./GrantFiltersLoader";
37
36
  import GrantRequest from "./GrantRequest";
38
37
  import GrantRequestLoader from "./GrantRequestLoader";
39
38
  import GrantRequestsLoader from "./GrantRequestsLoader";
40
39
  import GrantRequestsSearchResults from "./GrantRequestsSearchResults";
41
40
  import GrantRequestApprovalResults from "./GrantRequestApprovalResults";
42
41
  import Grants from "./Grants";
43
- import GrantsLoader from "./GrantsLoader";
44
42
  import MyGrantRequests from "./MyGrantRequests";
45
43
  import StructureGrantCartCheckout from "./StructureGrantCartCheckout";
46
44
  import StructuresGrantRequestView from "./StructuresGrantRequestView";
@@ -78,23 +76,11 @@ export const GrantRoutes = ({ grantRequestLoaded }) => {
78
76
  <Route path="/grants/:id" element={<GrantView />} />
79
77
  <Route
80
78
  path="/grants"
81
- element={
82
- <>
83
- <GrantsLoader />
84
- <GrantFiltersLoader />
85
- <Grants />
86
- </>
87
- }
79
+ element={<Grants />}
88
80
  />
89
81
  <Route
90
82
  path="/myGrants"
91
- element={
92
- <>
93
- <GrantsLoader onlyMyGrants />
94
- <GrantFiltersLoader onlyMyGrants />
95
- <Grants onlyMyGrants />
96
- </>
97
- }
83
+ element={<Grants onlyMyGrants />}
98
84
  />
99
85
  <Route
100
86
  path="/myGrantRequests"
@@ -1,19 +1,21 @@
1
+ import { useState } from "react";
1
2
  import PropTypes from "prop-types";
2
- import { connect } from "react-redux";
3
3
  import { Header, Icon, Segment, Dimmer, Loader } from "semantic-ui-react";
4
4
  import { FormattedMessage, useIntl } from "react-intl";
5
-
6
- import GrantDateFilter from "./GrantDateFilter";
7
- import GrantsSearch from "./GrantsSearch";
5
+ import { SearchContextProvider } from "@truedat/core/search/SearchContext";
6
+ import SearchWidget from "@truedat/core/search/SearchWidget";
7
+ import { useGrantFilters, useGrantSearch } from "../hooks/useGrant";
8
8
  import GrantsTable from "./GrantsTable";
9
9
  import GrantsLabelResults from "./GrantsLabelResults";
10
10
  import GrantsDownloadButton from "./GrantsDownloadButton";
11
-
12
- import GrantSelectedFilters from "./GrantSelectedFilters";
13
11
  import GrantsPagination from "./GrantsPagination";
12
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
14
13
 
15
- export const Grants = ({ grantsLoading, onlyMyGrants }) => {
14
+ export const Grants = ({ onlyMyGrants }) => {
16
15
  const { formatMessage } = useIntl();
16
+ const [downloading, setDownloading] = useState(false);
17
+ const { loading: grantsLoading } = useSearchContext();
18
+
17
19
  return (
18
20
  <Segment>
19
21
  <Header as="h2">
@@ -35,11 +37,14 @@ export const Grants = ({ grantsLoading, onlyMyGrants }) => {
35
37
  </Header.Subheader>
36
38
  </Header.Content>
37
39
  </Header>
38
- <Segment attached="bottom">
39
- <GrantsDownloadButton onlyMyGrants={onlyMyGrants} hasFilterApplied />
40
- <GrantsSearch />
41
- <GrantSelectedFilters />
42
- <GrantDateFilter />
40
+
41
+ <Segment attached="bottom" loading={downloading} disabled={downloading}>
42
+ <GrantsDownloadButton
43
+ onlyMyGrants={onlyMyGrants}
44
+ hasFilterApplied
45
+ setDownloading={setDownloading}
46
+ />
47
+ <SearchWidget dateFilter searchField={"start_date,end_date"} isGrantDateRange />
43
48
  <Dimmer.Dimmable dimmed={grantsLoading}>
44
49
  {grantsLoading ? (
45
50
  <Dimmer active inverted>
@@ -48,7 +53,7 @@ export const Grants = ({ grantsLoading, onlyMyGrants }) => {
48
53
  ) : null}
49
54
  </Dimmer.Dimmable>
50
55
  <GrantsLabelResults />
51
- <GrantsTable onlyMyGrants={onlyMyGrants} />
56
+ <GrantsTable onlyMyGrants={onlyMyGrants} setDownloading={setDownloading} />
52
57
  <GrantsPagination />
53
58
  </Segment>
54
59
  </Segment>
@@ -56,10 +61,32 @@ export const Grants = ({ grantsLoading, onlyMyGrants }) => {
56
61
  };
57
62
 
58
63
  Grants.propTypes = {
59
- grantsLoading: PropTypes.bool,
60
64
  onlyMyGrants: PropTypes.bool,
61
65
  };
62
66
 
63
- const mapStateToProps = ({ grantsLoading }) => ({ grantsLoading });
67
+ export const GrantsWrapper = ({ onlyMyGrants }) => {
68
+ const useSearch = useGrantSearch(onlyMyGrants);
69
+ const useFilters = useGrantFilters(onlyMyGrants);
70
+
71
+ const searchProps = {
72
+ initialSortColumn: "data_structure_version.name.raw",
73
+ initialSortDirection: "ascending",
74
+ useSearch: useSearch,
75
+ useFilters: useFilters,
76
+ pageSize: 20,
77
+ userFiltersType: "user_search_filters",
78
+ userFilterScope: onlyMyGrants ? "data_structure" : "",
79
+ };
80
+
81
+ return (
82
+ <SearchContextProvider key={onlyMyGrants ? "myGrants" : "allGrants"} {...searchProps}>
83
+ <Grants onlyMyGrants={onlyMyGrants} />
84
+ </SearchContextProvider>
85
+ );
86
+ };
87
+
88
+ GrantsWrapper.propTypes = {
89
+ onlyMyGrants: PropTypes.bool,
90
+ };
64
91
 
65
- export default connect(mapStateToProps)(Grants);
92
+ export default GrantsWrapper;
@@ -1,32 +1,49 @@
1
1
  import _ from "lodash/fp";
2
+ import { useEffect } from "react";
2
3
  import PropTypes from "prop-types";
3
- import { connect } from "react-redux";
4
4
  import { Button, Popup } from "semantic-ui-react";
5
5
  import { useIntl } from "react-intl";
6
- import { downloadGrants } from "../routines";
6
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
7
+ import { useGrantDownload } from "../hooks/useGrant";
7
8
 
8
9
  const staticLabels = [
9
10
  "user_name",
10
11
  "data_structure_name",
12
+ "domain_name",
13
+ "system_name",
14
+ "structure_path",
11
15
  "start_date",
12
16
  "end_date",
13
- "metadata",
14
- "mutable_metadata",
17
+ "grant_details",
15
18
  ];
16
19
 
17
20
  export const GrantsDownloadButton = ({
18
- grants,
19
- grantsDownloading,
20
- grantsLoading,
21
- downloadGrants,
22
21
  hasFilterApplied,
23
22
  onlyMyGrants,
23
+ setDownloading,
24
24
  }) => {
25
- const { formatMessage } = useIntl();
25
+ const { formatMessage, locale } = useIntl();
26
+
27
+ const {
28
+ searchData,
29
+ loading: grantsLoading,
30
+ filterParams: searchParams,
31
+ } = useSearchContext();
32
+
33
+ const { trigger: triggerDownload, isMutating: grantsDownloading } =
34
+ useGrantDownload();
35
+
36
+ useEffect(() => {
37
+ setDownloading(grantsDownloading);
38
+ }, [setDownloading, grantsDownloading]);
39
+
40
+ const grants = searchData?.data;
41
+
26
42
  const headerLabels = _.flow(
27
43
  _.map((l) => [l, formatMessage({ id: `grants.props.${l}` })]),
28
44
  _.fromPairs
29
45
  )(staticLabels);
46
+
30
47
  const messagePopup =
31
48
  hasFilterApplied && !_.isEmpty(grants)
32
49
  ? formatMessage({ id: "grants.actions.download.tooltip" })
@@ -44,9 +61,12 @@ export const GrantsDownloadButton = ({
44
61
  secondary
45
62
  icon="download"
46
63
  onClick={() =>
47
- downloadGrants({
64
+ triggerDownload({
48
65
  search_by: onlyMyGrants ? "user" : "permissions",
49
- headerLabels,
66
+ header_labels: headerLabels,
67
+ lang: locale,
68
+ loading: grantsLoading,
69
+ ...searchParams,
50
70
  })
51
71
  }
52
72
  loading={grantsDownloading}
@@ -61,18 +81,9 @@ export const GrantsDownloadButton = ({
61
81
  };
62
82
 
63
83
  GrantsDownloadButton.propTypes = {
64
- grantsDownloading: PropTypes.bool,
65
- grantsLoading: PropTypes.bool,
66
- downloadGrants: PropTypes.func,
67
84
  onlyMyGrants: PropTypes.bool,
85
+ hasFilterApplied: PropTypes.bool,
86
+ setDownloading: PropTypes.func,
68
87
  };
69
88
 
70
- const mapStateToProps = (state) => ({
71
- grantsDownloading: state.grantsDownloading,
72
- grantsLoading: state.grantsLoading,
73
- grants: state.grants,
74
- });
75
-
76
- export default connect(mapStateToProps, { downloadGrants })(
77
- GrantsDownloadButton
78
- );
89
+ export default GrantsDownloadButton;
@@ -1,9 +1,13 @@
1
- import PropTypes from "prop-types";
2
- import { connect } from "react-redux";
3
1
  import { FormattedMessage } from "react-intl";
4
2
  import { Label } from "semantic-ui-react";
3
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
4
+
5
+ export const GrantsLabelResults = ( ) => {
6
+ const {
7
+ loading: grantsLoading,
8
+ count: grantCount,
9
+ } = useSearchContext();
5
10
 
6
- export const GrantsLabelResults = ({ grantCount, grantsLoading }) => {
7
11
  return (
8
12
  <Label className="grants-label-results">
9
13
  {grantsLoading ? (
@@ -18,14 +22,4 @@ export const GrantsLabelResults = ({ grantCount, grantsLoading }) => {
18
22
  );
19
23
  };
20
24
 
21
- GrantsLabelResults.propTypes = {
22
- grantCount: PropTypes.number,
23
- grantsLoading: PropTypes.bool,
24
- };
25
-
26
- const mapStateToProps = ({ grantCount, grantsLoading }) => ({
27
- grantCount,
28
- grantsLoading,
29
- });
30
-
31
- export default connect(mapStateToProps)(GrantsLabelResults);
25
+ export default GrantsLabelResults;
@@ -1,14 +1,16 @@
1
- import { connect } from "react-redux";
2
- import { bindActionCreators } from "redux";
3
1
  import { Pagination } from "@truedat/core/components";
4
- import { selectGrantPage } from "../routines";
2
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
5
3
 
6
- const mapDispatchToProps = (dispatch) =>
7
- bindActionCreators({ selectPage: selectGrantPage }, dispatch);
4
+ export default function GrantsPagination() {
5
+ const { page, size, count, selectPage } = useSearchContext();
8
6
 
9
- const mapStateToProps = ({ grantQuery, grantCount, grantsPageSize }) => ({
10
- activePage: grantQuery?.page,
11
- totalPages: Math.ceil(grantCount / grantsPageSize),
12
- });
7
+ const totalPages = Math.max(1, Math.ceil(count / size));
13
8
 
14
- export default connect(mapStateToProps, mapDispatchToProps)(Pagination);
9
+ return (
10
+ <Pagination
11
+ activePage={page}
12
+ totalPages={totalPages}
13
+ selectPage={selectPage}
14
+ />
15
+ );
16
+ }
@@ -1,35 +1,45 @@
1
1
  import _ from "lodash/fp";
2
- import { useState } from "react";
2
+ import { useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { useIntl } from "react-intl";
5
5
  import { useNavigate } from "react-router";
6
6
  import { Table, Header, Icon } from "semantic-ui-react";
7
7
  import { connect } from "react-redux";
8
8
  import { columnDecoratorComponent } from "@truedat/core/services";
9
- import { getSortInfo, sortColumn } from "@truedat/core/services/sort";
10
9
  import { linkTo } from "@truedat/core/routes";
10
+ import { useSearchContext } from "@truedat/core/search/SearchContext";
11
+ import { useGrantDownload } from "../hooks/useGrant";
11
12
  import { getGrantsTableColumns } from "../selectors";
12
13
  import { sortGrants } from "../routines";
13
14
 
14
15
  export const GrantsTable = ({
15
16
  columns,
16
- grants,
17
- grantsLoading,
18
- sortGrants,
19
- grantsSorting,
17
+ setDownloading,
20
18
  }) => {
21
19
  const { formatMessage } = useIntl();
22
20
  const navigate = useNavigate();
21
+
22
+ const {
23
+ searchData,
24
+ loading: grantsLoading,
25
+ sortColumn: sortedColumn,
26
+ sortDirection: direction,
27
+ handleSortSelection,
28
+ } = useSearchContext();
29
+
30
+ const { isMutating: grantsDownloading } =
31
+ useGrantDownload();
32
+
33
+ useEffect(() => {
34
+ setDownloading(grantsDownloading);
35
+ }, [setDownloading, grantsDownloading]);
36
+
37
+ const grants = searchData?.data;
38
+
23
39
  const filteredColumns = _.filter((c) => _.any(_.prop(c.name))(grants))(
24
40
  columns
25
41
  );
26
42
 
27
- const [sortedColumn, setColumn] = useState(
28
- _.prop("column")(getSortInfo(grantsSorting))
29
- );
30
- const [direction, setDirection] = useState(
31
- _.prop("direction")(getSortInfo(grantsSorting))
32
- );
33
43
 
34
44
  return (
35
45
  <>
@@ -54,14 +64,7 @@ export const GrantsTable = ({
54
64
  className={_.path("sort.name")(column) ? "" : "disabled"}
55
65
  onClick={() => {
56
66
  filteredColumns.includes(column)
57
- ? sortColumn(
58
- column,
59
- sortGrants,
60
- setDirection,
61
- setColumn,
62
- direction,
63
- sortedColumn
64
- )
67
+ ? handleSortSelection(column.sort.name)
65
68
  : null;
66
69
  }}
67
70
  />
@@ -99,17 +102,10 @@ export const GrantsTable = ({
99
102
 
100
103
  GrantsTable.propTypes = {
101
104
  columns: PropTypes.array,
102
- grants: PropTypes.array,
103
- grantsLoading: PropTypes.bool,
104
- sortGrants: PropTypes.func,
105
- grantsSorting: PropTypes.array,
106
105
  };
107
106
 
108
107
  const mapStateToProps = (state, { onlyMyGrants }) => ({
109
108
  columns: getGrantsTableColumns({ ...state, onlyMyGrants }),
110
- grants: state.grants,
111
- grantsLoading: state.grantsLoading,
112
- grantsSorting: _.path("grantQuery.sort")(state),
113
109
  });
114
110
 
115
111
  export default connect(mapStateToProps, { sortGrants })(GrantsTable);
@@ -1,27 +1,168 @@
1
1
  import { render, waitForLoad } from "@truedat/test/render";
2
+ import { screen } from "@testing-library/react";
2
3
  import { GrantsDownloadButton } from "../GrantsDownloadButton";
4
+ import SearchContext from "@truedat/core/search/SearchContext";
5
+
6
+ jest.mock("@truedat/dd/hooks/useGrant", () => ({
7
+ useGrantDownload: jest.fn(() => ({
8
+ trigger: jest.fn(),
9
+ isMutating: false,
10
+ })),
11
+ }));
3
12
 
4
13
  describe("<GrantsDownloadButton />", () => {
5
14
  const renderOpts = {
6
15
  state: {
7
16
  userPermissions: { update: true },
8
17
  },
18
+ messages: {
19
+ en: {
20
+ "grants.actions.download.tooltip": "Download to XLSX",
21
+ "grants.props.data_structure_name": "Structure",
22
+ "grants.props.domain_name": "Domain",
23
+ "grants.props.end_date": "End date",
24
+ "grants.props.grant_details": "Details",
25
+ "grants.props.start_date": "Start date",
26
+ "grants.props.structure_path": "Path",
27
+ "grants.props.system_name": "System",
28
+ },
29
+ },
9
30
  };
10
31
 
11
- const downloadGrants = jest.fn();
12
- const grantsLoading = false;
32
+ const searchContext = {
33
+ searchData: {
34
+ data: [{
35
+ id: 1,
36
+ end_date: "2025-05-16",
37
+ start_date: "2025-01-01",
38
+ detail: {},
39
+ user: { full_name: "Jane Doe" },
40
+ data_structure_version: {
41
+ name: "country",
42
+ path: ["postgres", "public"],
43
+ domain: {name: "demo", id: 5},
44
+ system: {name: "PostgreSQL", id: 7},
45
+ external_id: "postgres://postgresql/postgres/public/country",
46
+ data_structure_id: 241939
47
+ }
48
+ }],
49
+ },
50
+ loading: false,
51
+ filterParams: {},
52
+ };
13
53
 
14
54
  it("matches the latest snapshot", async () => {
15
- const props = { downloadGrants, grantsLoading };
16
- const rendered = render(<GrantsDownloadButton {...props} />, renderOpts);
55
+ const props = { hasFilterApplied: true, onlyMyGrants: false, setDownloading: jest.fn() };
56
+
57
+ const rendered = render(
58
+ <SearchContext value={searchContext}>
59
+ <GrantsDownloadButton {...props}/>
60
+ </SearchContext>,
61
+ renderOpts
62
+ );
63
+
17
64
  await waitForLoad(rendered);
18
65
  expect(rendered.container).toMatchSnapshot();
19
66
  });
20
67
 
21
- it("is disabled if there are no structures to be downloaded", async () => {
22
- const props = { downloadGrants, grantsLoading, grants: [] };
23
- const rendered = render(<GrantsDownloadButton {...props} />, renderOpts);
24
- await waitForLoad(rendered);
25
- expect(rendered.container.querySelector("button")).toBeDisabled();
68
+ it("is enabled if there is any grant to be downloaded", async () => {
69
+ const props = { hasFilterApplied: true, onlyMyGrants: false, setDownloading: jest.fn() };
70
+
71
+ render(
72
+ <SearchContext value={searchContext}>
73
+ <GrantsDownloadButton {...props}/>
74
+ </SearchContext>,
75
+ renderOpts
76
+ );
77
+
78
+ const button = await screen.findByRole("button");
79
+ expect(button).toBeEnabled();
80
+ });
81
+
82
+ it("is disabled if there are no grants to be downloaded", async () => {
83
+ const searchContext = {
84
+ searchData: {
85
+ data: []
86
+ },
87
+ loading: false,
88
+ filterParams: {},
89
+ };
90
+
91
+ const props = { hasFilterApplied: false, onlyMyGrants: false, setDownloading: jest.fn() };
92
+
93
+ render(
94
+ <SearchContext value={searchContext}>
95
+ <GrantsDownloadButton {...props}/>
96
+ </SearchContext>,
97
+ renderOpts
98
+ );
99
+
100
+ const button = await screen.getByRole("button");
101
+ expect(button).toBeDisabled();
102
+ });
103
+
104
+ it("calls triggerDownload with the correct parameters on click", async () => {
105
+ const triggerMock = jest.fn();
106
+
107
+ jest.mocked(require("@truedat/dd/hooks/useGrant").useGrantDownload).mockReturnValue({
108
+ trigger: triggerMock,
109
+ isMutating: false,
110
+ });
111
+
112
+ const props = { hasFilterApplied: true, onlyMyGrants: true, setDownloading: jest.fn() };
113
+
114
+ render(
115
+ <SearchContext value={searchContext}>
116
+ <GrantsDownloadButton {...props} />
117
+ </SearchContext>,
118
+ renderOpts
119
+ );
120
+
121
+ const button = await screen.findByRole("button");
122
+ button.click();
123
+
124
+ expect(triggerMock).toHaveBeenCalledWith(
125
+ expect.objectContaining({
126
+ search_by: "user",
127
+ header_labels: expect.any(Object),
128
+ lang: "en",
129
+ })
130
+ );
131
+ });
132
+
133
+ it("calls setDownloading when isMutating changes", async () => {
134
+ const setDownloadingMock = jest.fn();
135
+
136
+ const grantHook = require("@truedat/dd/hooks/useGrant");
137
+
138
+ const props = { hasFilterApplied: true, onlyMyGrants: false, setDownloading: setDownloadingMock };
139
+
140
+ grantHook.useGrantDownload.mockReturnValue({
141
+ trigger: jest.fn(),
142
+ isMutating: false,
143
+ });
144
+
145
+ const { rerender } = render(
146
+ <SearchContext value={searchContext} >
147
+ <GrantsDownloadButton {...props} />
148
+ </SearchContext>,
149
+ renderOpts
150
+ );
151
+
152
+ grantHook.useGrantDownload.mockReturnValue({
153
+ trigger: jest.fn(),
154
+ isMutating: true,
155
+ });
156
+
157
+ const newProps = { hasFilterApplied: true, onlyMyGrants: false, setDownloading: setDownloadingMock };
158
+
159
+ rerender(
160
+ <SearchContext value={searchContext} >
161
+ <GrantsDownloadButton {...newProps} />
162
+ </SearchContext>,
163
+ renderOpts
164
+ );
165
+
166
+ expect(setDownloadingMock).toHaveBeenCalledWith(true);
26
167
  });
27
168
  });
@@ -1,5 +1,6 @@
1
1
  import { render, waitForLoad } from "@truedat/test/render";
2
2
  import { GrantsLabelResults } from "../GrantsLabelResults";
3
+ import SearchContext from "@truedat/core/search/SearchContext";
3
4
 
4
5
  describe("<GrantsLabelResults />", () => {
5
6
  const renderOpts = {
@@ -7,10 +8,20 @@ describe("<GrantsLabelResults />", () => {
7
8
  userPermissions: { update: true },
8
9
  },
9
10
  };
11
+ const grantsLoading = false;
12
+ const searchContext = {
13
+ count: 22,
14
+ loading: grantsLoading,
15
+ };
10
16
 
11
17
  it("matches the latest snapshot", async () => {
12
18
  const props = { grantCount: 22 };
13
- const rendered = render(<GrantsLabelResults {...props} />, renderOpts);
19
+ const rendered = render(
20
+ <SearchContext value={searchContext}>
21
+ <GrantsLabelResults {...props} />
22
+ </SearchContext>,
23
+ renderOpts
24
+ );
14
25
  await waitForLoad(rendered);
15
26
  expect(rendered.container).toMatchSnapshot();
16
27
  });