@dhis2-ui/organisation-unit-tree 7.16.2 → 8.0.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.
Files changed (55) hide show
  1. package/build/cjs/__e2e__/common.js +51 -24
  2. package/build/cjs/__e2e__/tree_api.stories.e2e.js +13 -5
  3. package/build/cjs/__stories__/development-stories.js +1 -3
  4. package/build/cjs/__stories__/shared.js +50 -17
  5. package/build/cjs/features/controlled_expanded/index.js +9 -3
  6. package/build/cjs/features/tree_api/index.js +4 -3
  7. package/build/cjs/helpers/sort-node-children-alphabetically.js +2 -17
  8. package/build/cjs/locales/ar/translations.json +1 -0
  9. package/build/cjs/locales/cs/translations.json +1 -0
  10. package/build/cjs/locales/en/translations.json +3 -2
  11. package/build/cjs/locales/es/translations.json +1 -0
  12. package/build/cjs/locales/nb/translations.json +1 -0
  13. package/build/cjs/locales/uz/translations.json +1 -0
  14. package/build/cjs/locales/uz_Latn/translations.json +1 -0
  15. package/build/cjs/locales/vi/translations.json +1 -0
  16. package/build/cjs/locales/zh/translations.json +1 -0
  17. package/build/cjs/locales/zh_CN/translations.json +1 -0
  18. package/build/cjs/organisation-unit-node/label/label.js +2 -5
  19. package/build/cjs/organisation-unit-node/loading-spinner.js +32 -0
  20. package/build/cjs/organisation-unit-node/organisation-unit-node-children.js +120 -0
  21. package/build/cjs/organisation-unit-node/organisation-unit-node.js +43 -68
  22. package/build/cjs/organisation-unit-node/use-org-children.js +81 -0
  23. package/build/cjs/organisation-unit-node/use-org-children.test.js +319 -0
  24. package/build/cjs/organisation-unit-node/use-org-data/use-org-data.js +10 -33
  25. package/build/cjs/organisation-unit-node/use-org-data/use-org-data.test.js +3 -169
  26. package/build/cjs/organisation-unit-tree/organisation-unit-tree.js +4 -2
  27. package/build/cjs/organisation-unit-tree/use-root-org-data/use-root-org-data.js +7 -5
  28. package/build/es/__e2e__/common.js +52 -24
  29. package/build/es/__e2e__/tree_api.stories.e2e.js +13 -5
  30. package/build/es/__stories__/development-stories.js +1 -3
  31. package/build/es/__stories__/shared.js +51 -17
  32. package/build/es/features/controlled_expanded/index.js +9 -3
  33. package/build/es/features/tree_api/index.js +4 -3
  34. package/build/es/helpers/sort-node-children-alphabetically.js +2 -17
  35. package/build/es/locales/ar/translations.json +1 -0
  36. package/build/es/locales/cs/translations.json +1 -0
  37. package/build/es/locales/en/translations.json +3 -2
  38. package/build/es/locales/es/translations.json +1 -0
  39. package/build/es/locales/nb/translations.json +1 -0
  40. package/build/es/locales/uz/translations.json +1 -0
  41. package/build/es/locales/uz_Latn/translations.json +1 -0
  42. package/build/es/locales/vi/translations.json +1 -0
  43. package/build/es/locales/zh/translations.json +1 -0
  44. package/build/es/locales/zh_CN/translations.json +1 -0
  45. package/build/es/organisation-unit-node/label/label.js +2 -5
  46. package/build/es/organisation-unit-node/loading-spinner.js +17 -0
  47. package/build/es/organisation-unit-node/organisation-unit-node-children.js +103 -0
  48. package/build/es/organisation-unit-node/organisation-unit-node.js +41 -65
  49. package/build/es/organisation-unit-node/use-org-children.js +69 -0
  50. package/build/es/organisation-unit-node/use-org-children.test.js +311 -0
  51. package/build/es/organisation-unit-node/use-org-data/use-org-data.js +10 -31
  52. package/build/es/organisation-unit-node/use-org-data/use-org-data.test.js +3 -169
  53. package/build/es/organisation-unit-tree/organisation-unit-tree.js +4 -2
  54. package/build/es/organisation-unit-tree/use-root-org-data/use-root-org-data.js +7 -5
  55. package/package.json +5 -5
@@ -1,4 +1,5 @@
1
1
  {
2
- "Could not load children": "",
3
- "Error: {{ ERRORMESSAGE }}": ""
2
+ "No children match filter": "No children match filter",
3
+ "Could not load children": "Could not load children",
4
+ "Error: {{ ERRORMESSAGE }}": "Error: {{ ERRORMESSAGE }}"
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "No se pudo cargar los elementos inferiores",
3
4
  "Error: {{ ERRORMESSAGE }}": "Error: {{ERRORMESSAGE}}"
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "Kunne ikke laste underordnede enheter",
3
4
  "Error: {{ ERRORMESSAGE }}": "Feil: {{ ERRORMESSAGE }}"
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "Болалар юкланмади",
3
4
  "Error: {{ ERRORMESSAGE }}": ""
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "Bolalar yuklanmadi",
3
4
  "Error: {{ ERRORMESSAGE }}": ""
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "Không thể tải đơn vị con",
3
4
  "Error: {{ ERRORMESSAGE }}": ""
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "无法加载下属",
3
4
  "Error: {{ ERRORMESSAGE }}": "错误:{{ ERRORMESSAGE }}"
4
5
  }
@@ -1,4 +1,5 @@
1
1
  {
2
+ "No children match filter": "",
2
3
  "Could not load children": "无法加载下属",
3
4
  "Error: {{ ERRORMESSAGE }}": ""
4
5
  }
@@ -71,7 +71,7 @@ const Label = ({
71
71
  singleSelection,
72
72
  rootId
73
73
  }); // @TODO: It'd make more sense to pass the node as an object
74
- // isntead of spread it. But that'd be a breaking change
74
+ // instead of spread it. But that'd be a breaking change
75
75
 
76
76
  const payload = { ...node,
77
77
  path: fullPath,
@@ -125,10 +125,7 @@ Label.propTypes = {
125
125
  node: PropTypes.shape({
126
126
  displayName: PropTypes.string.isRequired,
127
127
  id: PropTypes.string.isRequired,
128
- children: PropTypes.arrayOf(PropTypes.shape({
129
- displayName: PropTypes.string.isRequired,
130
- id: PropTypes.string.isRequired
131
- })),
128
+ children: PropTypes.number,
132
129
  path: PropTypes.string
133
130
  }).isRequired,
134
131
  open: PropTypes.bool.isRequired,
@@ -0,0 +1,17 @@
1
+ import _JSXStyle from "styled-jsx/style";
2
+ import { CircularLoader } from '@dhis2-ui/loader';
3
+ import React from 'react';
4
+ const loadingSpinnerStyles = {
5
+ styles: /*#__PURE__*/React.createElement(_JSXStyle, {
6
+ id: "358163430"
7
+ }, [".extrasmall.jsx-358163430{display:block;margin:3px 0;}"]),
8
+ className: "jsx-358163430"
9
+ };
10
+ export const LoadingSpinner = () => /*#__PURE__*/React.createElement("div", {
11
+ className: "jsx-2503342345"
12
+ }, /*#__PURE__*/React.createElement(CircularLoader, {
13
+ extrasmall: true,
14
+ className: loadingSpinnerStyles.className
15
+ }), /*#__PURE__*/React.createElement("style", null, loadingSpinnerStyles.styles), /*#__PURE__*/React.createElement(_JSXStyle, {
16
+ id: "2503342345"
17
+ }, ["div.jsx-2503342345{width:24px;}"]));
@@ -0,0 +1,103 @@
1
+ import PropTypes from 'prop-types';
2
+ import React from 'react';
3
+ import { isPathIncluded } from '../helpers/index.js';
4
+ import i18n from '../locales/index.js';
5
+ import { orgUnitPathPropType } from '../prop-types.js';
6
+ import { LoadingSpinner } from './loading-spinner.js';
7
+ import { useOrgChildren } from './use-org-children.js';
8
+
9
+ const getFilteredChildren = ({
10
+ orgChildren,
11
+ filter,
12
+ node
13
+ }) => {
14
+ if (!(filter !== null && filter !== void 0 && filter.length)) {
15
+ return orgChildren;
16
+ }
17
+
18
+ return orgChildren.filter(child => {
19
+ return isPathIncluded(filter, `${node.path}/${child.id}`);
20
+ });
21
+ };
22
+
23
+ export const OrganisationUnitNodeChildren = ({
24
+ node,
25
+ autoExpandLoadingError,
26
+ dataTest,
27
+ disableSelection,
28
+ expanded,
29
+ filter,
30
+ highlighted,
31
+ isUserDataViewFallback,
32
+ onChange,
33
+ onChildrenLoaded,
34
+ onCollapse,
35
+ onExpand,
36
+ parentPath,
37
+ renderNodeLabel,
38
+ rootId,
39
+ selected,
40
+ singleSelection,
41
+ suppressAlphabeticalSorting,
42
+ OrganisationUnitNode
43
+ }) => {
44
+ const orgChildren = useOrgChildren({
45
+ node,
46
+ isUserDataViewFallback,
47
+ suppressAlphabeticalSorting,
48
+ onComplete: onChildrenLoaded
49
+ });
50
+ const displayChildren = orgChildren.called && !orgChildren.loading && !orgChildren.error;
51
+ const filteredChildren = displayChildren ? getFilteredChildren({
52
+ orgChildren: orgChildren.data,
53
+ filter,
54
+ node
55
+ }) : [];
56
+ return /*#__PURE__*/React.createElement(React.Fragment, null, orgChildren.loading && /*#__PURE__*/React.createElement(LoadingSpinner, null), orgChildren.error && `Error: ${orgChildren.error}`, displayChildren && !filteredChildren.length && i18n.t('No children match filter'), !!filteredChildren.length && filteredChildren.map(child => {
57
+ const childPath = `${parentPath}/${child.id}`;
58
+ return /*#__PURE__*/React.createElement(OrganisationUnitNode, {
59
+ autoExpandLoadingError: autoExpandLoadingError,
60
+ dataTest: dataTest,
61
+ disableSelection: disableSelection,
62
+ displayName: child.displayName,
63
+ expanded: expanded,
64
+ filter: filter,
65
+ highlighted: highlighted,
66
+ id: child.id,
67
+ isUserDataViewFallback: isUserDataViewFallback,
68
+ key: childPath,
69
+ onChange: onChange,
70
+ onChildrenLoaded: onChildrenLoaded,
71
+ onCollapse: onCollapse,
72
+ onExpand: onExpand,
73
+ path: childPath,
74
+ renderNodeLabel: renderNodeLabel,
75
+ rootId: rootId,
76
+ selected: selected,
77
+ singleSelection: singleSelection,
78
+ suppressAlphabeticalSorting: suppressAlphabeticalSorting
79
+ });
80
+ }));
81
+ };
82
+ OrganisationUnitNodeChildren.propTypes = {
83
+ // Prevent cirular imports
84
+ OrganisationUnitNode: PropTypes.func.isRequired,
85
+ dataTest: PropTypes.string.isRequired,
86
+ node: PropTypes.object.isRequired,
87
+ parentPath: PropTypes.string.isRequired,
88
+ renderNodeLabel: PropTypes.func.isRequired,
89
+ rootId: PropTypes.string.isRequired,
90
+ onChange: PropTypes.func.isRequired,
91
+ autoExpandLoadingError: PropTypes.bool,
92
+ disableSelection: PropTypes.bool,
93
+ expanded: PropTypes.arrayOf(orgUnitPathPropType),
94
+ filter: PropTypes.arrayOf(orgUnitPathPropType),
95
+ highlighted: PropTypes.arrayOf(orgUnitPathPropType),
96
+ isUserDataViewFallback: PropTypes.bool,
97
+ selected: PropTypes.arrayOf(orgUnitPathPropType),
98
+ singleSelection: PropTypes.bool,
99
+ suppressAlphabeticalSorting: PropTypes.bool,
100
+ onChildrenLoaded: PropTypes.func,
101
+ onCollapse: PropTypes.func,
102
+ onExpand: PropTypes.func
103
+ };
@@ -1,33 +1,16 @@
1
- import _JSXStyle from "styled-jsx/style";
2
- import { CircularLoader } from '@dhis2-ui/loader';
3
1
  import { Node } from '@dhis2-ui/node';
4
2
  import PropTypes from 'prop-types';
5
3
  import React from 'react';
6
4
  import { leftTrimToRootId } from '../helpers/index.js';
7
5
  import i18n from '../locales/index.js';
8
6
  import { orgUnitPathPropType } from '../prop-types.js';
9
- import { computeChildNodes } from './compute-child-nodes.js';
10
7
  import { ErrorMessage } from './error-message.js';
11
8
  import { hasDescendantSelectedPaths } from './has-descendant-selected-paths.js';
12
9
  import { Label } from './label/index.js';
10
+ import { LoadingSpinner } from './loading-spinner.js';
11
+ import { OrganisationUnitNodeChildren } from './organisation-unit-node-children.js';
13
12
  import { useOpenState } from './use-open-state.js';
14
13
  import { useOrgData } from './use-org-data/index.js';
15
- const loadingSpinnerStyles = {
16
- styles: /*#__PURE__*/React.createElement(_JSXStyle, {
17
- id: "358163430"
18
- }, [".extrasmall.jsx-358163430{display:block;margin:3px 0;}"]),
19
- className: "jsx-358163430"
20
- };
21
-
22
- const LoadingSpinner = () => /*#__PURE__*/React.createElement("div", {
23
- className: "jsx-2503342345"
24
- }, /*#__PURE__*/React.createElement(CircularLoader, {
25
- extrasmall: true,
26
- className: loadingSpinnerStyles.className
27
- }), /*#__PURE__*/React.createElement("style", null, loadingSpinnerStyles.styles), /*#__PURE__*/React.createElement(_JSXStyle, {
28
- id: "2503342345"
29
- }, ["div.jsx-2503342345{width:24px;}"]));
30
-
31
14
  export const OrganisationUnitNode = ({
32
15
  autoExpandLoadingError,
33
16
  dataTest,
@@ -49,22 +32,20 @@ export const OrganisationUnitNode = ({
49
32
  onCollapse,
50
33
  onExpand
51
34
  }) => {
52
- const {
53
- loading,
54
- error,
55
- data
56
- } = useOrgData(id, {
35
+ const orgData = useOrgData(id, {
57
36
  isUserDataViewFallback,
58
- suppressAlphabeticalSorting,
59
- displayName,
60
- onComplete: onChildrenLoaded
37
+ displayName
61
38
  });
62
39
  const strippedPath = leftTrimToRootId(path, rootId);
63
- const node = { ...data,
40
+ const node = {
41
+ // guarantee that displayName and id are avaiable before data loaded
42
+ displayName,
43
+ id,
44
+ ...(orgData.data || {}),
45
+ // do not override strippedPath with path from loaded data
64
46
  path: strippedPath
65
47
  };
66
- const childNodes = !loading && !error ? computeChildNodes(node, filter) : [];
67
- const hasChildren = !!childNodes.length;
48
+ const hasChildren = !!node.children && node.children > 0;
68
49
  const hasSelectedDescendants = hasDescendantSelectedPaths(strippedPath, selected, rootId);
69
50
  const isHighlighted = highlighted.includes(path);
70
51
  const {
@@ -72,7 +53,7 @@ export const OrganisationUnitNode = ({
72
53
  onToggleOpen
73
54
  } = useOpenState({
74
55
  autoExpandLoadingError,
75
- errorMessage: error && error.toString(),
56
+ errorMessage: orgData.error && orgData.error.toString(),
76
57
  path: strippedPath,
77
58
  expanded,
78
59
  onExpand,
@@ -83,14 +64,14 @@ export const OrganisationUnitNode = ({
83
64
  disableSelection,
84
65
  hasChildren,
85
66
  hasSelectedDescendants,
86
- loading,
87
- error,
67
+ loading: orgData.loading,
68
+ error: orgData.error,
88
69
  selected,
89
70
  open,
90
71
  path,
91
72
  singleSelection,
92
73
  node,
93
- label: node.displayName,
74
+ label: displayName,
94
75
  checked: isSelected,
95
76
  highlighted: isHighlighted
96
77
  });
@@ -98,7 +79,7 @@ export const OrganisationUnitNode = ({
98
79
  node: node,
99
80
  fullPath: path,
100
81
  open: open,
101
- loading: loading,
82
+ loading: orgData.loading,
102
83
  checked: isSelected,
103
84
  rootId: rootId,
104
85
  onChange: onChange,
@@ -122,45 +103,40 @@ export const OrganisationUnitNode = ({
122
103
  * 4. Child nodes: There are children and the node is open
123
104
  */
124
105
 
125
- const showPlaceholder = hasChildren && !open && !error;
126
- const showChildNodes = hasChildren && open && !error;
106
+ const showPlaceholder = hasChildren && !open && !orgData.error;
107
+ const showChildNodes = hasChildren && open && !orgData.error;
127
108
  return /*#__PURE__*/React.createElement(Node, {
128
109
  dataTest: `${dataTest}-node`,
129
110
  open: open,
130
111
  onOpen: onToggleOpen,
131
112
  onClose: onToggleOpen,
132
113
  component: label,
133
- icon: loading && /*#__PURE__*/React.createElement(LoadingSpinner, null)
134
- }, error && /*#__PURE__*/React.createElement(ErrorMessage, {
114
+ icon: orgData.loading && /*#__PURE__*/React.createElement(LoadingSpinner, null)
115
+ }, orgData.error && /*#__PURE__*/React.createElement(ErrorMessage, {
135
116
  dataTest: dataTest
136
117
  }, i18n.t('Could not load children')), showPlaceholder && /*#__PURE__*/React.createElement("span", {
137
118
  "data-test": `${dataTest}-placeholder`
138
- }), showChildNodes && childNodes.map(child => {
139
- const childPath = `${path}/${child.id}`;
140
- const grandChildNodes = computeChildNodes(child, filter);
141
- return /*#__PURE__*/React.createElement(OrganisationUnitNode, {
142
- autoExpandLoadingError: autoExpandLoadingError,
143
- childNodes: grandChildNodes,
144
- dataTest: dataTest,
145
- disableSelection: disableSelection,
146
- displayName: child.displayName,
147
- expanded: expanded,
148
- filter: filter,
149
- highlighted: highlighted,
150
- id: child.id,
151
- isUserDataViewFallback: isUserDataViewFallback,
152
- key: childPath,
153
- onChange: onChange,
154
- onChildrenLoaded: onChildrenLoaded,
155
- onCollapse: onCollapse,
156
- onExpand: onExpand,
157
- path: childPath,
158
- renderNodeLabel: renderNodeLabel,
159
- rootId: rootId,
160
- selected: selected,
161
- singleSelection: singleSelection,
162
- suppressAlphabeticalSorting: suppressAlphabeticalSorting
163
- });
119
+ }), showChildNodes && /*#__PURE__*/React.createElement(OrganisationUnitNodeChildren // Prevent cirular imports
120
+ , {
121
+ OrganisationUnitNode: OrganisationUnitNode,
122
+ node: node,
123
+ autoExpandLoadingError: autoExpandLoadingError,
124
+ dataTest: dataTest,
125
+ disableSelection: disableSelection,
126
+ expanded: expanded,
127
+ filter: filter,
128
+ highlighted: highlighted,
129
+ isUserDataViewFallback: isUserDataViewFallback,
130
+ onChange: onChange,
131
+ onChildrenLoaded: onChildrenLoaded,
132
+ onCollapse: onCollapse,
133
+ onExpand: onExpand,
134
+ parentPath: path,
135
+ renderNodeLabel: renderNodeLabel,
136
+ rootId: rootId,
137
+ selected: selected,
138
+ singleSelection: singleSelection,
139
+ suppressAlphabeticalSorting: suppressAlphabeticalSorting
164
140
  }));
165
141
  };
166
142
  OrganisationUnitNode.propTypes = {
@@ -0,0 +1,69 @@
1
+ import { useDataQuery } from '@dhis2/app-runtime';
2
+ import { useMemo, useEffect, useRef } from 'react';
3
+ import { sortNodeChildrenAlphabetically } from '../helpers/index.js';
4
+ const ORG_DATA_QUERY = {
5
+ orgUnit: {
6
+ resource: `organisationUnits`,
7
+ id: ({
8
+ id
9
+ }) => id,
10
+ params: {
11
+ fields: 'children[id,path,displayName]'
12
+ }
13
+ }
14
+ };
15
+ /**
16
+ * @param {string[]} ids
17
+ * @param {Object} options
18
+ * @param {string} options.displayName
19
+ * @param {boolean} [options.withChildren]
20
+ * @returns {Object}
21
+ */
22
+
23
+ export const useOrgChildren = ({
24
+ node,
25
+ suppressAlphabeticalSorting,
26
+ onComplete
27
+ }) => {
28
+ const onCompleteCalledRef = useRef(false);
29
+ const {
30
+ called,
31
+ loading,
32
+ error,
33
+ data
34
+ } = useDataQuery(ORG_DATA_QUERY, {
35
+ variables: {
36
+ id: node.id
37
+ }
38
+ });
39
+ const orgChildren = useMemo(() => {
40
+ if (!data) {
41
+ return undefined;
42
+ } // undefined or zero
43
+
44
+
45
+ if (!node.children) {
46
+ return [];
47
+ }
48
+
49
+ const {
50
+ orgUnit
51
+ } = data;
52
+ return suppressAlphabeticalSorting ? orgUnit.children : sortNodeChildrenAlphabetically(orgUnit.children);
53
+ }, [data, suppressAlphabeticalSorting]);
54
+ useEffect(() => {
55
+ if (onComplete && orgChildren && !onCompleteCalledRef.current) {
56
+ // For backwards compatibility: Pass entire node incl. children
57
+ onComplete({ ...node,
58
+ children: orgChildren
59
+ });
60
+ onCompleteCalledRef.current = true;
61
+ }
62
+ }, [onComplete, orgChildren, onCompleteCalledRef]);
63
+ return {
64
+ called,
65
+ loading,
66
+ error: error || null,
67
+ data: orgChildren
68
+ };
69
+ };