@truedat/dd 5.0.7 → 5.1.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.1.0] 2023-02-09
4
+
5
+ ### Changed
6
+
7
+ - [TD-5444] Search system root structures
8
+
3
9
  ## [5.0.7] 2023-02-07
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/dd",
3
- "version": "5.0.7",
3
+ "version": "5.1.1",
4
4
  "description": "Truedat Web Data Dictionary",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -114,5 +114,5 @@
114
114
  "react-dom": ">= 16.8.6 < 17",
115
115
  "semantic-ui-react": ">= 2.0.3 < 2.2"
116
116
  },
117
- "gitHead": "5919ee71f696378a71010642f5bcd2fb6c68f1c7"
117
+ "gitHead": "28731e6c6de05ad746edb43d5a23f99c6e0a5779"
118
118
  }
@@ -2,6 +2,7 @@ import _ from "lodash/fp";
2
2
  import React from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { List, Divider } from "semantic-ui-react";
5
+ import { useIntl } from "react-intl";
5
6
  import StructureItemRoot from "./StructureItemRoot";
6
7
  import StructureItem from "./StructureItem";
7
8
  import StructureItems from "./StructureItems";
@@ -14,37 +15,56 @@ export const navClasses = _.flow(
14
15
  );
15
16
  export const hasNavClass = _.flow(navClasses, _.negate(_.isEmpty));
16
17
 
17
- export const StructureNav = ({ childStructures, parentStructure }) => {
18
+ export const StructureNav = ({
19
+ childStructures,
20
+ parentStructure,
21
+ totalChildStructures,
22
+ }) => {
23
+ const { formatMessage } = useIntl();
18
24
  const firstWithClasses = _.find(hasNavClass)(childStructures);
19
25
  const classifier = firstWithClasses
20
26
  ? _.flow(navClasses, _.head)(firstWithClasses)
21
27
  : undefined;
22
28
 
23
29
  return (
24
- <List relaxed selection className="structure-nav">
25
- <StructureItemRoot />
26
- {_.isEmpty(parentStructure) ? null : (
27
- <>
28
- <Divider />
29
- <StructureItem {...parentStructure} />
30
- <Divider />
31
- </>
32
- )}
33
- {classifier ? (
34
- <StructureNavClassified
35
- classifier={classifier}
36
- structures={childStructures}
37
- />
38
- ) : (
39
- <StructureItems structures={childStructures} />
40
- )}
41
- </List>
30
+ <>
31
+ <List relaxed selection className="structure-nav">
32
+ <StructureItemRoot />
33
+ {_.isEmpty(parentStructure) ? null : (
34
+ <>
35
+ <Divider />
36
+ <StructureItem {...parentStructure} />
37
+ <Divider />
38
+ </>
39
+ )}
40
+ {classifier ? (
41
+ <StructureNavClassified
42
+ classifier={classifier}
43
+ structures={childStructures}
44
+ />
45
+ ) : (
46
+ <StructureItems structures={childStructures} />
47
+ )}
48
+ </List>
49
+ {totalChildStructures && totalChildStructures > childStructures.length ? (
50
+ <List>
51
+ <List.Item disabled active={false}>
52
+ <span>
53
+ {`${childStructures.length} ${formatMessage({
54
+ id: "structure.navigation.showing",
55
+ })} ${totalChildStructures}`}
56
+ </span>
57
+ </List.Item>
58
+ </List>
59
+ ) : null}
60
+ </>
42
61
  );
43
62
  };
44
63
 
45
64
  StructureNav.propTypes = {
46
65
  childStructures: PropTypes.array,
47
- parentStructure: PropTypes.object,
66
+ parentStructure: PropTypes.array,
67
+ totalChildStructures: PropTypes.number,
48
68
  };
49
69
 
50
70
  export default StructureNav;
@@ -3,9 +3,9 @@ import PropTypes from "prop-types";
3
3
  import { useIntl } from "react-intl";
4
4
  import { Input } from "semantic-ui-react";
5
5
 
6
- export const StructureSearch = ({ onChange }) => {
6
+ export const StructureSearch = ({ onChange, loading = false }) => {
7
7
  const { formatMessage } = useIntl();
8
- const handleChange = e => {
8
+ const handleChange = (e) => {
9
9
  onChange(e.target.value);
10
10
  };
11
11
  return (
@@ -14,13 +14,15 @@ export const StructureSearch = ({ onChange }) => {
14
14
  icon={{ name: "search", link: true }}
15
15
  iconPosition="left"
16
16
  fluid
17
+ loading={loading}
17
18
  placeholder={formatMessage({ id: "structure.search.placeholder" })}
18
19
  />
19
20
  );
20
21
  };
21
22
 
22
23
  StructureSearch.propTypes = {
23
- onChange: PropTypes.func
24
+ onChange: PropTypes.func,
25
+ loading: PropTypes.bool,
24
26
  };
25
27
 
26
28
  export default StructureSearch;
@@ -0,0 +1,32 @@
1
+ import _ from "lodash/fp";
2
+ import React, { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+ import { useParams } from "react-router-dom";
5
+ import { useStructures } from "../hooks/useStructures";
6
+ import StructureNav from "./StructureNav";
7
+ import StructureSearch from "./StructureSearch";
8
+
9
+ const SystemFilteredNav = ({ delay = 500 }) => {
10
+ const { id: systemId } = useParams();
11
+ const [query, setQuery] = useState();
12
+ const { data, isLoading, count } = useStructures({ systemId, query });
13
+
14
+ return (
15
+ <>
16
+ <StructureSearch
17
+ onChange={_.debounce(delay)(setQuery)}
18
+ loading={isLoading}
19
+ />
20
+ <StructureNav
21
+ childStructures={data || []}
22
+ parentStructure={[]}
23
+ totalChildStructures={count}
24
+ />
25
+ </>
26
+ );
27
+ };
28
+ SystemFilteredNav.propTypes = {
29
+ delay: PropTypes.number,
30
+ };
31
+
32
+ export default SystemFilteredNav;
@@ -4,14 +4,14 @@ import PropTypes from "prop-types";
4
4
  import { connect } from "react-redux";
5
5
  import { Grid, Segment } from "semantic-ui-react";
6
6
  import { getSystem, getSystemTemplate } from "../selectors";
7
- import FilteredNav from "./FilteredNav";
7
+ import SystemFilteredNav from "./SystemFilteredNav";
8
8
  import SystemCrumbs from "./SystemCrumbs";
9
9
  import SystemView from "./SystemView";
10
10
 
11
11
  export const SystemStructures = ({
12
12
  system,
13
13
  systemStructuresLoading,
14
- systemTemplate
14
+ systemTemplate,
15
15
  }) =>
16
16
  !systemStructuresLoading && (
17
17
  <Grid>
@@ -23,7 +23,7 @@ export const SystemStructures = ({
23
23
  <Grid.Row stretched>
24
24
  <Grid.Column width={4}>
25
25
  <Segment>
26
- <FilteredNav parentStructures={[]} />
26
+ <SystemFilteredNav />
27
27
  </Segment>
28
28
  </Grid.Column>
29
29
  <Grid.Column width={12}>
@@ -36,13 +36,13 @@ export const SystemStructures = ({
36
36
  SystemStructures.propTypes = {
37
37
  system: PropTypes.object,
38
38
  systemStructuresLoading: PropTypes.bool,
39
- systemTemplate: PropTypes.object
39
+ systemTemplate: PropTypes.object,
40
40
  };
41
41
 
42
- const mapStateToProps = state => ({
42
+ const mapStateToProps = (state) => ({
43
43
  system: getSystem(state),
44
44
  systemStructuresLoading: _.prop("systemStructuresLoading")(state),
45
- systemTemplate: getSystemTemplate(state)
45
+ systemTemplate: getSystemTemplate(state),
46
46
  });
47
47
 
48
48
  export default connect(mapStateToProps)(SystemStructures);
@@ -4,7 +4,10 @@ import { StructureNav, hasNavClass, navClasses } from "../StructureNav";
4
4
 
5
5
  const renderOpts = {
6
6
  messages: {
7
- en: { "structure.navigation.home": "Home" },
7
+ en: {
8
+ "structure.navigation.home": "Home",
9
+ "structure.navigation.showing": "from a total of",
10
+ },
8
11
  },
9
12
  };
10
13
 
@@ -24,6 +27,15 @@ describe("<StructureNav />", () => {
24
27
  const { container } = render(<StructureNav {...props} />, renderOpts);
25
28
  expect(container).toMatchSnapshot();
26
29
  });
30
+
31
+ it("matches the latest snapshot with total greater than showed structures", () => {
32
+ const childStructures = [{ id: 1, classes: { foo: "bar" } }];
33
+ const totalChildStructures = 10;
34
+ const props = { childStructures, totalChildStructures };
35
+
36
+ const { container } = render(<StructureNav {...props} />, renderOpts);
37
+ expect(container).toMatchSnapshot();
38
+ });
27
39
  });
28
40
 
29
41
  describe("navClasses", () => {
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { render } from "@truedat/test/render";
3
+ import SystemFilteredNav from "../SystemFilteredNav";
4
+
5
+ jest.mock("../../hooks/useStructures.js", () => {
6
+ const originalModule = jest.requireActual("../../hooks/useStructures.js");
7
+
8
+ return {
9
+ __esModule: true,
10
+ ...originalModule,
11
+ useStructures: jest.fn(() => ({
12
+ data: [
13
+ { id: 1, name: "bank_capital" },
14
+ { id: 2, name: "consumorenovables" },
15
+ ],
16
+ count: 2,
17
+ })),
18
+ };
19
+ });
20
+
21
+ describe("<SystemFilteredNav />", () => {
22
+ it("matches the latest snapshot", () => {
23
+ const { container } = render(<SystemFilteredNav />, {});
24
+ expect(container).toMatchSnapshot();
25
+ });
26
+ });
@@ -96,3 +96,65 @@ exports[`<StructureNav /> matches the latest snapshot with classified structures
96
96
  </div>
97
97
  </div>
98
98
  `;
99
+
100
+ exports[`<StructureNav /> matches the latest snapshot with total greater than showed structures 1`] = `
101
+ <div>
102
+ <div
103
+ class="ui selection relaxed list structure-nav"
104
+ role="list"
105
+ >
106
+ <div
107
+ class="item"
108
+ role="listitem"
109
+ >
110
+ <i
111
+ aria-hidden="true"
112
+ class="angle double left icon middle aligned"
113
+ />
114
+ <div
115
+ class="content"
116
+ >
117
+ Home
118
+ </div>
119
+ <div
120
+ class="content"
121
+ >
122
+ <a
123
+ class="description"
124
+ />
125
+ </div>
126
+ </div>
127
+ <div
128
+ class="item"
129
+ role="listitem"
130
+ >
131
+ <i
132
+ aria-hidden="true"
133
+ class="right triangle icon"
134
+ />
135
+ <div
136
+ class="content"
137
+ >
138
+ <div
139
+ class="header noselect"
140
+ >
141
+ bar
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ <div
147
+ class="ui list"
148
+ role="list"
149
+ >
150
+ <div
151
+ class="disabled item"
152
+ role="listitem"
153
+ >
154
+ <span>
155
+ 1 from a total of 10
156
+ </span>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ `;
@@ -10,6 +10,7 @@ exports[`<StructureSearch/> matches the latest snapshot 1`] = `
10
10
  }
11
11
  }
12
12
  iconPosition="left"
13
+ loading={false}
13
14
  onChange={[Function]}
14
15
  placeholder="structure.search.placeholder"
15
16
  type="text"
@@ -0,0 +1,84 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<SystemFilteredNav /> matches the latest snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="ui fluid left icon input"
7
+ >
8
+ <input
9
+ placeholder="Enter a search..."
10
+ type="text"
11
+ />
12
+ <i
13
+ aria-hidden="true"
14
+ class="search link icon"
15
+ />
16
+ </div>
17
+ <div
18
+ class="ui selection relaxed list structure-nav"
19
+ role="list"
20
+ >
21
+ <div
22
+ class="item"
23
+ role="listitem"
24
+ >
25
+ <i
26
+ aria-hidden="true"
27
+ class="angle double left icon middle aligned"
28
+ />
29
+ <div
30
+ class="content"
31
+ >
32
+ Home
33
+ </div>
34
+ <div
35
+ class="content"
36
+ >
37
+ <a
38
+ class="description"
39
+ />
40
+ </div>
41
+ </div>
42
+ <a
43
+ class="item"
44
+ href="/structures/1"
45
+ role="listitem"
46
+ title="bank_capital"
47
+ >
48
+ <i
49
+ aria-hidden="true"
50
+ class="question circle outline icon middle aligned"
51
+ />
52
+ <div
53
+ class="content"
54
+ >
55
+ <div
56
+ class="description"
57
+ >
58
+ bank_capital
59
+ </div>
60
+ </div>
61
+ </a>
62
+ <a
63
+ class="item"
64
+ href="/structures/2"
65
+ role="listitem"
66
+ title="consumorenovables"
67
+ >
68
+ <i
69
+ aria-hidden="true"
70
+ class="question circle outline icon middle aligned"
71
+ />
72
+ <div
73
+ class="content"
74
+ >
75
+ <div
76
+ class="description"
77
+ >
78
+ consumorenovables
79
+ </div>
80
+ </div>
81
+ </a>
82
+ </div>
83
+ </div>
84
+ `;
@@ -16,9 +16,7 @@ exports[`<SystemStructures /> matches the latest snapshot 1`] = `
16
16
  width={4}
17
17
  >
18
18
  <Segment>
19
- <Connect(FilteredNav)
20
- parentStructures={Array []}
21
- />
19
+ <SystemFilteredNav />
22
20
  </Segment>
23
21
  </GridColumn>
24
22
  <GridColumn
@@ -0,0 +1,41 @@
1
+ import _ from "lodash/fp";
2
+ import { compile } from "path-to-regexp";
3
+ import useSWR from "swr";
4
+ import { apiJson } from "@truedat/core/services/api";
5
+ import { API_SYSTEM_STRUCTURES } from "../api";
6
+
7
+ const toApiPath = compile(API_SYSTEM_STRUCTURES);
8
+
9
+ const allowedQueryParams = ["query"];
10
+
11
+ const asQueryParams = (queryParams) => {
12
+ const stringifyQueryParams = _.flow(
13
+ _.pick(allowedQueryParams),
14
+ _.entries,
15
+ _.map(([key, value]) =>
16
+ _.isEmpty(value) ? "" : `${encodeURI(key)}=${encodeURI(value)}`
17
+ ),
18
+ _.join("&")
19
+ )(queryParams);
20
+ return _.isEmpty(stringifyQueryParams) ? "" : `?${stringifyQueryParams}`;
21
+ };
22
+
23
+ export const useStructures = (args) => {
24
+ console.log(args);
25
+ const url = toApiPath({ id: args.systemId });
26
+ const requestData = () => apiJson(`${url}${asQueryParams(args)}`);
27
+ const options = { keepPreviousData: true };
28
+
29
+ const { data, isLoading, error } = useSWR(
30
+ { url, args },
31
+ requestData,
32
+ options
33
+ );
34
+
35
+ return {
36
+ data: data?.data?.data,
37
+ isLoading,
38
+ error,
39
+ count: parseInt(data?.headers["x-total-count"]),
40
+ };
41
+ };
@@ -330,6 +330,7 @@ export default {
330
330
  "structure.metadata._protected": "Protected metadata",
331
331
  "structure.name": "Structure",
332
332
  "structure.navigation.home": "Home",
333
+ "structure.navigation.showing": "from a total of",
333
334
  "structure.no_description": "No definition",
334
335
  "structure.notes.actions.deleted": "Delete",
335
336
  "structure.notes.actions.deleted.confirmation.content":
@@ -339,6 +339,7 @@ export default {
339
339
  "structure.metadata._protected": "Metadata protegida",
340
340
  "structure.name": "Estructura",
341
341
  "structure.navigation.home": "Inicio",
342
+ "structure.navigation.showing": "mostrados de",
342
343
  "structure.no_description": "Sin descripción",
343
344
  "structure.notes.actions.deleted": "Eliminar",
344
345
  "structure.notes.actions.deleted.confirmation.content":