@dhis2-ui/organisation-unit-tree 10.16.4 → 10.16.5

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.
@@ -7,15 +7,16 @@ exports.OrganisationUnitTree = void 0;
7
7
  var _propTypes = require("@dhis2/prop-types");
8
8
  var _propTypes2 = _interopRequireDefault(require("prop-types"));
9
9
  var _react = _interopRequireWildcard(require("react"));
10
- var _index = require("../organisation-unit-node/index.js");
10
+ var _index = require("../helpers/index.js");
11
+ var _index2 = require("../organisation-unit-node/index.js");
11
12
  var _propTypes3 = require("../prop-types.js");
12
- var _index2 = require("./default-render-node-label/index.js");
13
+ var _index3 = require("./default-render-node-label/index.js");
13
14
  var _filterRootIds = require("./filter-root-ids.js");
14
15
  var _organisationUnitTreeRootError = require("./organisation-unit-tree-root-error.js");
15
16
  var _organisationUnitTreeRootLoading = require("./organisation-unit-tree-root-loading.js");
16
- var _index3 = require("./use-expanded/index.js");
17
+ var _index4 = require("./use-expanded/index.js");
17
18
  var _useForceReload = require("./use-force-reload.js");
18
- var _index4 = require("./use-root-org-data/index.js");
19
+ var _index5 = require("./use-root-org-data/index.js");
19
20
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
20
21
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
22
  // A stable object to reference
@@ -32,7 +33,7 @@ const OrganisationUnitTree = ({
32
33
  isUserDataViewFallback,
33
34
  initiallyExpanded = staticArray,
34
35
  filter = staticArray,
35
- renderNodeLabel = _index2.defaultRenderNodeLabel,
36
+ renderNodeLabel = _index3.defaultRenderNodeLabel,
36
37
  selected = staticArray,
37
38
  singleSelection,
38
39
  suppressAlphabeticalSorting,
@@ -52,7 +53,7 @@ const OrganisationUnitTree = ({
52
53
  error,
53
54
  data,
54
55
  refetch
55
- } = (0, _index4.useRootOrgData)(rootIds, {
56
+ } = (0, _index5.useRootOrgData)(rootIds, {
56
57
  isUserDataViewFallback,
57
58
  suppressAlphabeticalSorting,
58
59
  displayProperty
@@ -61,7 +62,7 @@ const OrganisationUnitTree = ({
61
62
  expanded,
62
63
  handleExpand,
63
64
  handleCollapse
64
- } = (0, _index3.useExpanded)({
65
+ } = (0, _index4.useExpanded)({
65
66
  initiallyExpanded,
66
67
  onExpand,
67
68
  onCollapse,
@@ -78,13 +79,20 @@ const OrganisationUnitTree = ({
78
79
  return () => console.warn('@TODO: Why does this component unmount after a force reload?');
79
80
  }, [reloadId, prevReloadId, refetch]);
80
81
  const isLoading = !called || loading;
82
+ const sortedRoots = (0, _react.useMemo)(() => {
83
+ if (!data) {
84
+ return staticArray;
85
+ }
86
+ const rootNodes = rootIds.map(rootId => data[rootId]);
87
+ return suppressAlphabeticalSorting ? rootNodes : (0, _index.sortNodeChildrenAlphabetically)(rootNodes);
88
+ }, [data, rootIds, suppressAlphabeticalSorting]);
81
89
  return /*#__PURE__*/_react.default.createElement("div", {
82
90
  "data-test": dataTest
83
91
  }, isLoading && /*#__PURE__*/_react.default.createElement(_organisationUnitTreeRootLoading.OrganisationUnitTreeRootLoading, null), error && /*#__PURE__*/_react.default.createElement(_organisationUnitTreeRootError.OrganisationUnitTreeRootError, {
84
92
  error: error
85
- }), !error && !isLoading && rootIds.map(rootId => {
86
- const rootNode = data[rootId];
87
- return /*#__PURE__*/_react.default.createElement(_index.OrganisationUnitNode, {
93
+ }), !error && !isLoading && sortedRoots.map(rootNode => {
94
+ const rootId = rootNode.id;
95
+ return /*#__PURE__*/_react.default.createElement(_index2.OrganisationUnitNode, {
88
96
  key: rootNode.path,
89
97
  rootId: rootId,
90
98
  autoExpandLoadingError: autoExpandLoadingError,
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  var _appRuntime = require("@dhis2/app-runtime");
4
+ var _react = require("@testing-library/react");
4
5
  var _enzyme = require("enzyme");
5
- var _react = _interopRequireDefault(require("react"));
6
+ var _react2 = _interopRequireDefault(require("react"));
6
7
  var _organisationUnitTree = require("./organisation-unit-tree.js");
7
8
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
9
  describe('OrganisationUnitTree', () => {
@@ -18,9 +19,9 @@ describe('OrganisationUnitTree', () => {
18
19
  describe('Controlled expanded props', () => {
19
20
  describe('Missing props', () => {
20
21
  it('should throw a prop-types error when "handleCollapse" is missing', () => {
21
- (0, _enzyme.shallow)(/*#__PURE__*/_react.default.createElement(_appRuntime.CustomDataProvider, {
22
+ (0, _enzyme.shallow)(/*#__PURE__*/_react2.default.createElement(_appRuntime.CustomDataProvider, {
22
23
  data: {}
23
- }, /*#__PURE__*/_react.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
24
+ }, /*#__PURE__*/_react2.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
24
25
  roots: "/A001",
25
26
  expanded: [],
26
27
  onChange: () => {},
@@ -30,9 +31,9 @@ describe('OrganisationUnitTree', () => {
30
31
  expect(errorMock.mock.calls[0][2]).toMatch(/Invalid prop `handleCollapse` supplied to `OrganisationUnitTree`/, {});
31
32
  });
32
33
  it('should throw a prop-types error when "handleExpand" is missing', () => {
33
- (0, _enzyme.shallow)(/*#__PURE__*/_react.default.createElement(_appRuntime.CustomDataProvider, {
34
+ (0, _enzyme.shallow)(/*#__PURE__*/_react2.default.createElement(_appRuntime.CustomDataProvider, {
34
35
  data: {}
35
- }, /*#__PURE__*/_react.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
36
+ }, /*#__PURE__*/_react2.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
36
37
  roots: "/A001",
37
38
  expanded: [],
38
39
  onChange: () => {},
@@ -42,9 +43,9 @@ describe('OrganisationUnitTree', () => {
42
43
  expect(errorMock.mock.calls[0][2]).toMatch(/Invalid prop `handleExpand` supplied to `OrganisationUnitTree`/);
43
44
  });
44
45
  it('should throw a prop-types error when "expanded" is missing', () => {
45
- (0, _enzyme.shallow)(/*#__PURE__*/_react.default.createElement(_appRuntime.CustomDataProvider, {
46
+ (0, _enzyme.shallow)(/*#__PURE__*/_react2.default.createElement(_appRuntime.CustomDataProvider, {
46
47
  data: {}
47
- }, /*#__PURE__*/_react.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
48
+ }, /*#__PURE__*/_react2.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
48
49
  roots: "/A001",
49
50
  onChange: () => {},
50
51
  handleCollapse: () => {},
@@ -55,4 +56,74 @@ describe('OrganisationUnitTree', () => {
55
56
  });
56
57
  });
57
58
  });
59
+ describe('Root node sorting', () => {
60
+ // displayNames intentionally out of alphabetical order relative to ids
61
+ const rootNodes = {
62
+ A001: {
63
+ id: 'A001',
64
+ path: '/A001',
65
+ displayName: 'Bravo'
66
+ },
67
+ A002: {
68
+ id: 'A002',
69
+ path: '/A002',
70
+ displayName: 'Alpha'
71
+ },
72
+ A003: {
73
+ id: 'A003',
74
+ path: '/A003',
75
+ displayName: 'Charlie'
76
+ }
77
+ };
78
+ const dataProvider = {
79
+ organisationUnits: (type, {
80
+ id
81
+ }) => {
82
+ const node = rootNodes[id];
83
+ if (node) {
84
+ // children: 0 so nodes don't try to load descendants
85
+ return {
86
+ ...node,
87
+ children: 0
88
+ };
89
+ }
90
+ return Promise.reject(`No org unit with id "${id}"`);
91
+ }
92
+ };
93
+ it('sorts root nodes alphabetically by displayName by default', async () => {
94
+ const {
95
+ container,
96
+ findByText
97
+ } = (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_appRuntime.CustomDataProvider, {
98
+ data: dataProvider
99
+ }, /*#__PURE__*/_react2.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
100
+ roots: ['A001', 'A002', 'A003'],
101
+ onChange: () => {}
102
+ })));
103
+ await findByText('Alpha');
104
+ const {
105
+ textContent
106
+ } = container;
107
+ expect(textContent.indexOf('Alpha')).toBeLessThan(textContent.indexOf('Bravo'));
108
+ expect(textContent.indexOf('Bravo')).toBeLessThan(textContent.indexOf('Charlie'));
109
+ });
110
+ it('keeps the provided roots order when suppressAlphabeticalSorting is true', async () => {
111
+ const {
112
+ container,
113
+ findByText
114
+ } = (0, _react.render)(/*#__PURE__*/_react2.default.createElement(_appRuntime.CustomDataProvider, {
115
+ data: dataProvider
116
+ }, /*#__PURE__*/_react2.default.createElement(_organisationUnitTree.OrganisationUnitTree, {
117
+ roots: ['A003', 'A001', 'A002'],
118
+ suppressAlphabeticalSorting: true,
119
+ onChange: () => {}
120
+ })));
121
+ await findByText('Charlie');
122
+ const {
123
+ textContent
124
+ } = container;
125
+ expect(textContent.indexOf('Charlie')).toBeLessThan(textContent.indexOf('Bravo'));
126
+ expect(textContent.indexOf('Bravo')).toBeLessThan(textContent.indexOf('Alpha'));
127
+ });
128
+ });
58
129
  });
@@ -1,6 +1,7 @@
1
1
  import { requiredIf } from '@dhis2/prop-types';
2
2
  import PropTypes from 'prop-types';
3
- import React, { useEffect, useState } from 'react';
3
+ import React, { useEffect, useMemo, useState } from 'react';
4
+ import { sortNodeChildrenAlphabetically } from '../helpers/index.js';
4
5
  import { OrganisationUnitNode } from '../organisation-unit-node/index.js';
5
6
  import { orgUnitPathPropType } from '../prop-types.js';
6
7
  import { defaultRenderNodeLabel } from './default-render-node-label/index.js';
@@ -71,12 +72,19 @@ const OrganisationUnitTree = ({
71
72
  return () => console.warn('@TODO: Why does this component unmount after a force reload?');
72
73
  }, [reloadId, prevReloadId, refetch]);
73
74
  const isLoading = !called || loading;
75
+ const sortedRoots = useMemo(() => {
76
+ if (!data) {
77
+ return staticArray;
78
+ }
79
+ const rootNodes = rootIds.map(rootId => data[rootId]);
80
+ return suppressAlphabeticalSorting ? rootNodes : sortNodeChildrenAlphabetically(rootNodes);
81
+ }, [data, rootIds, suppressAlphabeticalSorting]);
74
82
  return /*#__PURE__*/React.createElement("div", {
75
83
  "data-test": dataTest
76
84
  }, isLoading && /*#__PURE__*/React.createElement(OrganisationUnitTreeRootLoading, null), error && /*#__PURE__*/React.createElement(OrganisationUnitTreeRootError, {
77
85
  error: error
78
- }), !error && !isLoading && rootIds.map(rootId => {
79
- const rootNode = data[rootId];
86
+ }), !error && !isLoading && sortedRoots.map(rootNode => {
87
+ const rootId = rootNode.id;
80
88
  return /*#__PURE__*/React.createElement(OrganisationUnitNode, {
81
89
  key: rootNode.path,
82
90
  rootId: rootId,
@@ -1,4 +1,5 @@
1
1
  import { CustomDataProvider } from '@dhis2/app-runtime';
2
+ import { render } from '@testing-library/react';
2
3
  import { shallow } from 'enzyme';
3
4
  import React from 'react';
4
5
  import { OrganisationUnitTree } from './organisation-unit-tree.js';
@@ -52,4 +53,74 @@ describe('OrganisationUnitTree', () => {
52
53
  });
53
54
  });
54
55
  });
56
+ describe('Root node sorting', () => {
57
+ // displayNames intentionally out of alphabetical order relative to ids
58
+ const rootNodes = {
59
+ A001: {
60
+ id: 'A001',
61
+ path: '/A001',
62
+ displayName: 'Bravo'
63
+ },
64
+ A002: {
65
+ id: 'A002',
66
+ path: '/A002',
67
+ displayName: 'Alpha'
68
+ },
69
+ A003: {
70
+ id: 'A003',
71
+ path: '/A003',
72
+ displayName: 'Charlie'
73
+ }
74
+ };
75
+ const dataProvider = {
76
+ organisationUnits: (type, {
77
+ id
78
+ }) => {
79
+ const node = rootNodes[id];
80
+ if (node) {
81
+ // children: 0 so nodes don't try to load descendants
82
+ return {
83
+ ...node,
84
+ children: 0
85
+ };
86
+ }
87
+ return Promise.reject(`No org unit with id "${id}"`);
88
+ }
89
+ };
90
+ it('sorts root nodes alphabetically by displayName by default', async () => {
91
+ const {
92
+ container,
93
+ findByText
94
+ } = render(/*#__PURE__*/React.createElement(CustomDataProvider, {
95
+ data: dataProvider
96
+ }, /*#__PURE__*/React.createElement(OrganisationUnitTree, {
97
+ roots: ['A001', 'A002', 'A003'],
98
+ onChange: () => {}
99
+ })));
100
+ await findByText('Alpha');
101
+ const {
102
+ textContent
103
+ } = container;
104
+ expect(textContent.indexOf('Alpha')).toBeLessThan(textContent.indexOf('Bravo'));
105
+ expect(textContent.indexOf('Bravo')).toBeLessThan(textContent.indexOf('Charlie'));
106
+ });
107
+ it('keeps the provided roots order when suppressAlphabeticalSorting is true', async () => {
108
+ const {
109
+ container,
110
+ findByText
111
+ } = render(/*#__PURE__*/React.createElement(CustomDataProvider, {
112
+ data: dataProvider
113
+ }, /*#__PURE__*/React.createElement(OrganisationUnitTree, {
114
+ roots: ['A003', 'A001', 'A002'],
115
+ suppressAlphabeticalSorting: true,
116
+ onChange: () => {}
117
+ })));
118
+ await findByText('Charlie');
119
+ const {
120
+ textContent
121
+ } = container;
122
+ expect(textContent.indexOf('Charlie')).toBeLessThan(textContent.indexOf('Bravo'));
123
+ expect(textContent.indexOf('Bravo')).toBeLessThan(textContent.indexOf('Alpha'));
124
+ });
125
+ });
55
126
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2-ui/organisation-unit-tree",
3
- "version": "10.16.4",
3
+ "version": "10.16.5",
4
4
  "description": "UI OrganisationUnitTree",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,11 +35,11 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@dhis2/prop-types": "^3.1.2",
38
- "@dhis2-ui/button": "10.16.4",
39
- "@dhis2-ui/checkbox": "10.16.4",
40
- "@dhis2-ui/loader": "10.16.4",
41
- "@dhis2-ui/node": "10.16.4",
42
- "@dhis2/ui-constants": "10.16.4",
38
+ "@dhis2-ui/button": "10.16.5",
39
+ "@dhis2-ui/checkbox": "10.16.5",
40
+ "@dhis2-ui/loader": "10.16.5",
41
+ "@dhis2-ui/node": "10.16.5",
42
+ "@dhis2/ui-constants": "10.16.5",
43
43
  "classnames": "^2.3.1",
44
44
  "prop-types": "^15.7.2"
45
45
  },
@@ -1,6 +1,7 @@
1
1
  import { requiredIf } from '@dhis2/prop-types'
2
2
  import PropTypes from 'prop-types'
3
- import React, { useEffect, useState } from 'react'
3
+ import React, { useEffect, useMemo, useState } from 'react'
4
+ import { sortNodeChildrenAlphabetically } from '../helpers/index.js'
4
5
  import { OrganisationUnitNode } from '../organisation-unit-node/index.js'
5
6
  import { orgUnitPathPropType } from '../prop-types.js'
6
7
  import { defaultRenderNodeLabel } from './default-render-node-label/index.js'
@@ -75,14 +76,24 @@ const OrganisationUnitTree = ({
75
76
 
76
77
  const isLoading = !called || loading
77
78
 
79
+ const sortedRoots = useMemo(() => {
80
+ if (!data) {
81
+ return staticArray
82
+ }
83
+ const rootNodes = rootIds.map((rootId) => data[rootId])
84
+ return suppressAlphabeticalSorting
85
+ ? rootNodes
86
+ : sortNodeChildrenAlphabetically(rootNodes)
87
+ }, [data, rootIds, suppressAlphabeticalSorting])
88
+
78
89
  return (
79
90
  <div data-test={dataTest}>
80
91
  {isLoading && <OrganisationUnitTreeRootLoading />}
81
92
  {error && <OrganisationUnitTreeRootError error={error} />}
82
93
  {!error &&
83
94
  !isLoading &&
84
- rootIds.map((rootId) => {
85
- const rootNode = data[rootId]
95
+ sortedRoots.map((rootNode) => {
96
+ const rootId = rootNode.id
86
97
 
87
98
  return (
88
99
  <OrganisationUnitNode
@@ -1,4 +1,5 @@
1
1
  import { CustomDataProvider } from '@dhis2/app-runtime'
2
+ import { render } from '@testing-library/react'
2
3
  import { shallow } from 'enzyme'
3
4
  import React from 'react'
4
5
  import { OrganisationUnitTree } from './organisation-unit-tree.js'
@@ -74,4 +75,67 @@ describe('OrganisationUnitTree', () => {
74
75
  })
75
76
  })
76
77
  })
78
+
79
+ describe('Root node sorting', () => {
80
+ // displayNames intentionally out of alphabetical order relative to ids
81
+ const rootNodes = {
82
+ A001: { id: 'A001', path: '/A001', displayName: 'Bravo' },
83
+ A002: { id: 'A002', path: '/A002', displayName: 'Alpha' },
84
+ A003: { id: 'A003', path: '/A003', displayName: 'Charlie' },
85
+ }
86
+
87
+ const dataProvider = {
88
+ organisationUnits: (type, { id }) => {
89
+ const node = rootNodes[id]
90
+ if (node) {
91
+ // children: 0 so nodes don't try to load descendants
92
+ return { ...node, children: 0 }
93
+ }
94
+ return Promise.reject(`No org unit with id "${id}"`)
95
+ },
96
+ }
97
+
98
+ it('sorts root nodes alphabetically by displayName by default', async () => {
99
+ const { container, findByText } = render(
100
+ <CustomDataProvider data={dataProvider}>
101
+ <OrganisationUnitTree
102
+ roots={['A001', 'A002', 'A003']}
103
+ onChange={() => {}}
104
+ />
105
+ </CustomDataProvider>
106
+ )
107
+
108
+ await findByText('Alpha')
109
+
110
+ const { textContent } = container
111
+ expect(textContent.indexOf('Alpha')).toBeLessThan(
112
+ textContent.indexOf('Bravo')
113
+ )
114
+ expect(textContent.indexOf('Bravo')).toBeLessThan(
115
+ textContent.indexOf('Charlie')
116
+ )
117
+ })
118
+
119
+ it('keeps the provided roots order when suppressAlphabeticalSorting is true', async () => {
120
+ const { container, findByText } = render(
121
+ <CustomDataProvider data={dataProvider}>
122
+ <OrganisationUnitTree
123
+ roots={['A003', 'A001', 'A002']}
124
+ suppressAlphabeticalSorting
125
+ onChange={() => {}}
126
+ />
127
+ </CustomDataProvider>
128
+ )
129
+
130
+ await findByText('Charlie')
131
+
132
+ const { textContent } = container
133
+ expect(textContent.indexOf('Charlie')).toBeLessThan(
134
+ textContent.indexOf('Bravo')
135
+ )
136
+ expect(textContent.indexOf('Bravo')).toBeLessThan(
137
+ textContent.indexOf('Alpha')
138
+ )
139
+ })
140
+ })
77
141
  })