@dhis2-ui/organisation-unit-tree 7.2.8 → 7.4.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/build/cjs/__e2e__/common.js +134 -92
- package/build/cjs/__e2e__/controlled_expanded.stories.e2e.js +90 -0
- package/build/cjs/__e2e__/path_based_filtering.stories.e2e.js +0 -1
- package/build/cjs/__e2e__/single_selection.stories.e2e.js +4 -4
- package/build/cjs/__e2e__/tree_api.stories.e2e.js +6 -8
- package/build/cjs/__stories__/collapsed.js +22 -0
- package/build/cjs/__stories__/custom-expanded-imperative-open.js +184 -0
- package/build/cjs/__stories__/custom-node-label.js +33 -0
- package/build/cjs/__stories__/development-stories.js +96 -0
- package/build/cjs/__stories__/expanded.js +23 -0
- package/build/cjs/__stories__/filtered-root.js +25 -0
- package/build/cjs/__stories__/filtered.js +24 -0
- package/build/cjs/__stories__/force-reload-all.js +58 -0
- package/build/cjs/__stories__/force-reload-one-unit.js +37 -0
- package/build/cjs/__stories__/highlighted.js +24 -0
- package/build/cjs/__stories__/indeterminate.js +24 -0
- package/build/cjs/__stories__/loading-error-grandchild.js +44 -0
- package/build/cjs/__stories__/loading.js +39 -0
- package/build/cjs/__stories__/multiple-roots.js +24 -0
- package/build/cjs/__stories__/no-selection.js +26 -0
- package/build/cjs/__stories__/replace-roots.js +28 -0
- package/build/cjs/__stories__/root-error.js +48 -0
- package/build/cjs/__stories__/root-loading.js +48 -0
- package/build/cjs/__stories__/selected-multiple.js +25 -0
- package/build/cjs/__stories__/shared.js +140 -0
- package/build/cjs/__stories__/single-selection.js +26 -0
- package/build/cjs/features/controlled_expanded/index.js +60 -0
- package/build/cjs/features/controlled_expanded.feature +11 -0
- package/build/cjs/features/tree_api/index.js +1 -3
- package/build/cjs/get-all-expanded-paths/get-all-expanded-paths.js +40 -0
- package/build/cjs/get-all-expanded-paths/get-all-expanded-paths.test.js +17 -0
- package/build/cjs/get-all-expanded-paths/index.js +13 -0
- package/build/cjs/index.js +9 -1
- package/build/cjs/organisation-unit-node/use-open-state.js +11 -16
- package/build/cjs/organisation-unit-node/use-open-state.test.js +59 -101
- package/build/cjs/organisation-unit-tree/organisation-unit-tree.js +35 -20
- package/build/cjs/organisation-unit-tree/organisation-unit-tree.test.js +63 -0
- package/build/cjs/organisation-unit-tree/use-expanded/{helpers.js → create-expand-handlers.js} +1 -30
- package/build/cjs/organisation-unit-tree/use-expanded/{helpers.test.js → create-expand-handlers.test.js} +5 -18
- package/build/cjs/organisation-unit-tree/use-expanded/use-expanded.js +23 -4
- package/build/cjs/organisation-unit-tree/use-expanded/use-expanded.test.js +42 -8
- package/build/cjs/organisation-unit-tree.stories.js +191 -534
- package/build/es/__e2e__/common.js +121 -90
- package/build/es/__e2e__/controlled_expanded.stories.e2e.js +68 -0
- package/build/es/__e2e__/path_based_filtering.stories.e2e.js +0 -1
- package/build/es/__e2e__/single_selection.stories.e2e.js +4 -4
- package/build/es/__e2e__/tree_api.stories.e2e.js +6 -7
- package/build/es/__stories__/collapsed.js +8 -0
- package/build/es/__stories__/custom-expanded-imperative-open.js +166 -0
- package/build/es/__stories__/custom-node-label.js +19 -0
- package/build/es/__stories__/development-stories.js +73 -0
- package/build/es/__stories__/expanded.js +9 -0
- package/build/es/__stories__/filtered-root.js +11 -0
- package/build/es/__stories__/filtered.js +10 -0
- package/build/es/__stories__/force-reload-all.js +38 -0
- package/build/es/__stories__/force-reload-one-unit.js +25 -0
- package/build/es/__stories__/highlighted.js +10 -0
- package/build/es/__stories__/indeterminate.js +10 -0
- package/build/es/__stories__/loading-error-grandchild.js +29 -0
- package/build/es/__stories__/loading.js +24 -0
- package/build/es/__stories__/multiple-roots.js +10 -0
- package/build/es/__stories__/no-selection.js +12 -0
- package/build/es/__stories__/replace-roots.js +16 -0
- package/build/es/__stories__/root-error.js +33 -0
- package/build/es/__stories__/root-loading.js +33 -0
- package/build/es/__stories__/selected-multiple.js +11 -0
- package/build/es/__stories__/shared.js +120 -0
- package/build/es/__stories__/single-selection.js +12 -0
- package/build/es/features/controlled_expanded/index.js +57 -0
- package/build/es/features/controlled_expanded.feature +11 -0
- package/build/es/features/tree_api/index.js +1 -3
- package/build/es/get-all-expanded-paths/get-all-expanded-paths.js +31 -0
- package/build/es/get-all-expanded-paths/get-all-expanded-paths.test.js +14 -0
- package/build/es/get-all-expanded-paths/index.js +1 -0
- package/build/es/index.js +2 -1
- package/build/es/organisation-unit-node/use-open-state.js +11 -16
- package/build/es/organisation-unit-node/use-open-state.test.js +59 -94
- package/build/es/organisation-unit-tree/organisation-unit-tree.js +15 -1
- package/build/es/organisation-unit-tree/organisation-unit-tree.test.js +55 -0
- package/build/es/organisation-unit-tree/use-expanded/{helpers.js → create-expand-handlers.js} +0 -23
- package/build/es/organisation-unit-tree/use-expanded/{helpers.test.js → create-expand-handlers.test.js} +1 -14
- package/build/es/organisation-unit-tree/use-expanded/use-expanded.js +21 -3
- package/build/es/organisation-unit-tree/use-expanded/use-expanded.test.js +40 -7
- package/build/es/organisation-unit-tree.stories.js +23 -465
- package/package.json +5 -5
|
@@ -1,164 +1,129 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { renderHook } from '@testing-library/react-hooks';
|
|
2
2
|
import { useOpenState } from './use-open-state.js';
|
|
3
|
-
jest.mock('react', () => ({
|
|
4
|
-
useState: jest.fn(),
|
|
5
|
-
useEffect: jest.fn()
|
|
6
|
-
}));
|
|
7
3
|
describe('OrganisationUnitTree - useOpenState', () => {
|
|
8
4
|
const onExpand = jest.fn();
|
|
9
5
|
const onCollapse = jest.fn();
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
useState.mockImplementation(initialValue => [initialValue, () => null]);
|
|
12
|
-
useEffect.mockImplementation(callback => callback());
|
|
13
|
-
});
|
|
14
6
|
afterEach(() => {
|
|
15
7
|
onExpand.mockClear();
|
|
16
8
|
onCollapse.mockClear();
|
|
17
9
|
});
|
|
18
|
-
it('should
|
|
10
|
+
it('should set open to false if the path is not in the expanded array', () => {
|
|
19
11
|
const path = '/foo';
|
|
20
12
|
const expanded = ['/bar'];
|
|
21
13
|
const expected = false;
|
|
22
14
|
const {
|
|
23
|
-
|
|
24
|
-
} = useOpenState({
|
|
15
|
+
result
|
|
16
|
+
} = renderHook(() => useOpenState({
|
|
25
17
|
path,
|
|
26
18
|
expanded
|
|
27
|
-
});
|
|
19
|
+
}));
|
|
20
|
+
const {
|
|
21
|
+
open: actual
|
|
22
|
+
} = result.current;
|
|
28
23
|
expect(actual).toBe(expected);
|
|
29
24
|
});
|
|
30
|
-
it('should
|
|
25
|
+
it('should set open to true if the path is in the expanded array', () => {
|
|
31
26
|
const path = '/foo';
|
|
32
27
|
const expanded = ['/foo'];
|
|
33
28
|
const expected = true;
|
|
34
29
|
const {
|
|
35
|
-
|
|
36
|
-
} = useOpenState({
|
|
30
|
+
result
|
|
31
|
+
} = renderHook(() => useOpenState({
|
|
37
32
|
path,
|
|
38
33
|
expanded
|
|
39
|
-
});
|
|
34
|
+
}));
|
|
35
|
+
const {
|
|
36
|
+
open: actual
|
|
37
|
+
} = result.current;
|
|
40
38
|
expect(actual).toBe(expected);
|
|
41
39
|
});
|
|
42
|
-
it('should call
|
|
43
|
-
let useStateCall = 0;
|
|
44
|
-
const open = true;
|
|
45
|
-
const setOpen = jest.fn();
|
|
46
|
-
useState.mockImplementation(initialValue => {
|
|
47
|
-
useStateCall++;
|
|
48
|
-
|
|
49
|
-
if (useStateCall === 1) {
|
|
50
|
-
return [initialValue, jest.fn()];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return [open, setOpen];
|
|
54
|
-
});
|
|
40
|
+
it('should call onCollapse when calling onToggleOpen while open is true', () => {
|
|
55
41
|
const path = '/foo';
|
|
56
42
|
const expanded = ['/foo'];
|
|
57
43
|
const {
|
|
58
|
-
|
|
59
|
-
} = useOpenState({
|
|
44
|
+
result
|
|
45
|
+
} = renderHook(() => useOpenState({
|
|
60
46
|
path,
|
|
61
|
-
expanded
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
expect(setOpen).toHaveBeenCalledWith(!open);
|
|
65
|
-
});
|
|
66
|
-
it('should call setOpen with true when calling onToggleOpen if open is false', () => {
|
|
67
|
-
let useStateCall = 0;
|
|
68
|
-
const open = true;
|
|
69
|
-
const setOpen = jest.fn();
|
|
70
|
-
useState.mockImplementation(initialValue => {
|
|
71
|
-
useStateCall++;
|
|
72
|
-
|
|
73
|
-
if (useStateCall === 1) {
|
|
74
|
-
return [initialValue, jest.fn()];
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return [open, setOpen];
|
|
78
|
-
});
|
|
47
|
+
expanded,
|
|
48
|
+
onCollapse
|
|
49
|
+
}));
|
|
79
50
|
const {
|
|
80
51
|
onToggleOpen
|
|
81
|
-
} =
|
|
82
|
-
path: '/foo',
|
|
83
|
-
expanded: []
|
|
84
|
-
});
|
|
52
|
+
} = result.current;
|
|
85
53
|
onToggleOpen();
|
|
86
|
-
expect(
|
|
54
|
+
expect(onCollapse).toHaveBeenCalledWith({
|
|
55
|
+
path: '/foo'
|
|
56
|
+
});
|
|
57
|
+
expect(onExpand).toHaveBeenCalledTimes(0);
|
|
87
58
|
});
|
|
88
|
-
it('should call
|
|
89
|
-
useState.mockImplementation(() => [false, jest.fn()]);
|
|
59
|
+
it('should call onExpand when calling onToggleOpen while open is false', () => {
|
|
90
60
|
const path = '/foo';
|
|
61
|
+
const expanded = [];
|
|
91
62
|
const {
|
|
92
|
-
|
|
93
|
-
} = useOpenState({
|
|
63
|
+
result
|
|
64
|
+
} = renderHook(() => useOpenState({
|
|
94
65
|
path,
|
|
95
|
-
expanded
|
|
96
|
-
onExpand
|
|
97
|
-
|
|
98
|
-
});
|
|
99
|
-
onToggleOpen();
|
|
100
|
-
expect(onExpand).toHaveBeenCalledWith({
|
|
101
|
-
path
|
|
102
|
-
});
|
|
103
|
-
expect(onCollapse).toHaveBeenCalledTimes(0);
|
|
104
|
-
});
|
|
105
|
-
it('should call the onCollapse callback with the path when calling onToggleOpen and open was true', () => {
|
|
106
|
-
useState.mockImplementation(() => [true, jest.fn()]);
|
|
107
|
-
const path = '/foo';
|
|
66
|
+
expanded,
|
|
67
|
+
onExpand
|
|
68
|
+
}));
|
|
108
69
|
const {
|
|
109
70
|
onToggleOpen
|
|
110
|
-
} =
|
|
111
|
-
path,
|
|
112
|
-
expanded: [],
|
|
113
|
-
onExpand,
|
|
114
|
-
onCollapse
|
|
115
|
-
});
|
|
71
|
+
} = result.current;
|
|
116
72
|
onToggleOpen();
|
|
117
|
-
expect(
|
|
118
|
-
path
|
|
73
|
+
expect(onExpand).toHaveBeenCalledWith({
|
|
74
|
+
path: '/foo'
|
|
119
75
|
});
|
|
120
|
-
expect(
|
|
76
|
+
expect(onCollapse).toHaveBeenCalledTimes(0);
|
|
121
77
|
});
|
|
122
|
-
it('should set
|
|
78
|
+
it('should set openedOnceDueToError to true if there is an error and autoExpandLoadingError is true', async () => {
|
|
123
79
|
const path = '/foo';
|
|
124
80
|
const {
|
|
125
|
-
|
|
126
|
-
} = useOpenState({
|
|
81
|
+
result
|
|
82
|
+
} = renderHook(() => useOpenState({
|
|
127
83
|
autoExpandLoadingError: true,
|
|
128
84
|
errorMessage: 'error message',
|
|
129
85
|
path,
|
|
130
86
|
expanded: [],
|
|
131
87
|
onExpand,
|
|
132
88
|
onCollapse
|
|
133
|
-
});
|
|
134
|
-
|
|
89
|
+
}));
|
|
90
|
+
const {
|
|
91
|
+
openedOnceDueToError
|
|
92
|
+
} = result.current;
|
|
93
|
+
expect(openedOnceDueToError).toBe(true);
|
|
135
94
|
});
|
|
136
|
-
it('should not
|
|
95
|
+
it('should not set open to true if there is an error but autoExpandLoadingError is falsy', () => {
|
|
137
96
|
const path = '/foo';
|
|
138
97
|
const {
|
|
139
|
-
|
|
140
|
-
} = useOpenState({
|
|
98
|
+
result
|
|
99
|
+
} = renderHook(() => useOpenState({
|
|
141
100
|
autoExpandLoadingError: undefined,
|
|
142
101
|
errorMessage: 'error message',
|
|
143
102
|
path,
|
|
144
103
|
expanded: [],
|
|
145
104
|
onExpand,
|
|
146
105
|
onCollapse
|
|
147
|
-
});
|
|
106
|
+
}));
|
|
107
|
+
const {
|
|
108
|
+
open
|
|
109
|
+
} = result.current;
|
|
148
110
|
expect(open).toBe(false);
|
|
149
111
|
});
|
|
150
|
-
it('should not
|
|
112
|
+
it('should not set open to true if autoExpandLoadingError is true but there is no error', () => {
|
|
151
113
|
const path = '/foo';
|
|
152
114
|
const {
|
|
153
|
-
|
|
154
|
-
} = useOpenState({
|
|
115
|
+
result
|
|
116
|
+
} = renderHook(() => useOpenState({
|
|
155
117
|
autoExpandLoadingError: true,
|
|
156
118
|
errorMessage: '',
|
|
157
119
|
path,
|
|
158
120
|
expanded: [],
|
|
159
121
|
onExpand,
|
|
160
122
|
onCollapse
|
|
161
|
-
});
|
|
123
|
+
}));
|
|
124
|
+
const {
|
|
125
|
+
open
|
|
126
|
+
} = result.current;
|
|
162
127
|
expect(open).toBe(false);
|
|
163
128
|
});
|
|
164
129
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { requiredIf } from '@dhis2/prop-types';
|
|
1
2
|
import PropTypes from 'prop-types';
|
|
2
3
|
import React, { useEffect, useState } from 'react';
|
|
3
4
|
import { OrganisationUnitNode } from '../organisation-unit-node/index.js';
|
|
@@ -25,6 +26,9 @@ const OrganisationUnitTree = ({
|
|
|
25
26
|
selected,
|
|
26
27
|
singleSelection,
|
|
27
28
|
suppressAlphabeticalSorting,
|
|
29
|
+
expanded: expandedControlled,
|
|
30
|
+
handleExpand: handleExpandControlled,
|
|
31
|
+
handleCollapse: handleCollapseControlled,
|
|
28
32
|
onExpand,
|
|
29
33
|
onCollapse,
|
|
30
34
|
onChildrenLoaded
|
|
@@ -45,7 +49,14 @@ const OrganisationUnitTree = ({
|
|
|
45
49
|
expanded,
|
|
46
50
|
handleExpand,
|
|
47
51
|
handleCollapse
|
|
48
|
-
} = useExpanded(
|
|
52
|
+
} = useExpanded({
|
|
53
|
+
initiallyExpanded,
|
|
54
|
+
onExpand,
|
|
55
|
+
onCollapse,
|
|
56
|
+
expandedControlled,
|
|
57
|
+
handleExpandControlled,
|
|
58
|
+
handleCollapseControlled
|
|
59
|
+
});
|
|
49
60
|
useEffect(() => {
|
|
50
61
|
// do not refetch on initial render
|
|
51
62
|
if (refetch && reloadId > 0 && reloadId !== prevReloadId) {
|
|
@@ -103,6 +114,7 @@ OrganisationUnitTree.propTypes = {
|
|
|
103
114
|
|
|
104
115
|
/** When set to true, no unit can be selected */
|
|
105
116
|
disableSelection: PropTypes.bool,
|
|
117
|
+
expanded: requiredIf(props => !!props.handleExpand || !!props.handleCollapse, PropTypes.arrayOf(PropTypes.string)),
|
|
106
118
|
|
|
107
119
|
/**
|
|
108
120
|
* All organisation units with a path that includes the provided paths will be shown.
|
|
@@ -112,6 +124,8 @@ OrganisationUnitTree.propTypes = {
|
|
|
112
124
|
|
|
113
125
|
/** When true, everything will be reloaded. In order to load it again after reloading, `forceReload` has to be set to `false` and then to `true` again */
|
|
114
126
|
forceReload: PropTypes.bool,
|
|
127
|
+
handleCollapse: requiredIf(props => !!props.expanded || !!props.handleExpand, PropTypes.func),
|
|
128
|
+
handleExpand: requiredIf(props => !!props.expanded || !!props.handleCollapse, PropTypes.func),
|
|
115
129
|
|
|
116
130
|
/**
|
|
117
131
|
* All units provided to "highlighted" as path will be visually
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CustomDataProvider } from '@dhis2/app-runtime';
|
|
2
|
+
import { shallow } from 'enzyme';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { OrganisationUnitTree } from './organisation-unit-tree.js';
|
|
5
|
+
describe('OrganisationUnitTree', () => {
|
|
6
|
+
const origError = console.error.bind(console);
|
|
7
|
+
const errorMock = jest.fn();
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
console.error = errorMock;
|
|
10
|
+
});
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
console.error = origError;
|
|
13
|
+
errorMock.mockClear();
|
|
14
|
+
});
|
|
15
|
+
describe('Controlled expanded props', () => {
|
|
16
|
+
describe('Missing props', () => {
|
|
17
|
+
it('should throw a prop-types error when "handleCollapse" is missing', () => {
|
|
18
|
+
shallow( /*#__PURE__*/React.createElement(CustomDataProvider, {
|
|
19
|
+
data: {}
|
|
20
|
+
}, /*#__PURE__*/React.createElement(OrganisationUnitTree, {
|
|
21
|
+
roots: "/A001",
|
|
22
|
+
expanded: [],
|
|
23
|
+
onChange: () => {},
|
|
24
|
+
handleExpand: () => {}
|
|
25
|
+
})));
|
|
26
|
+
expect(errorMock).toHaveBeenCalledTimes(1);
|
|
27
|
+
expect(errorMock.mock.calls[0][0]).toMatch(/^Warning: Failed prop type: Invalid prop `handleCollapse` supplied to `OrganisationUnitTree`/);
|
|
28
|
+
});
|
|
29
|
+
it('should throw a prop-types error when "handleExpand" is missing', () => {
|
|
30
|
+
shallow( /*#__PURE__*/React.createElement(CustomDataProvider, {
|
|
31
|
+
data: {}
|
|
32
|
+
}, /*#__PURE__*/React.createElement(OrganisationUnitTree, {
|
|
33
|
+
roots: "/A001",
|
|
34
|
+
expanded: [],
|
|
35
|
+
onChange: () => {},
|
|
36
|
+
handleCollapse: () => {}
|
|
37
|
+
})));
|
|
38
|
+
expect(errorMock).toHaveBeenCalledTimes(1);
|
|
39
|
+
expect(errorMock.mock.calls[0][0]).toMatch(/^Warning: Failed prop type: Invalid prop `handleExpand` supplied to `OrganisationUnitTree`/);
|
|
40
|
+
});
|
|
41
|
+
it('should throw a prop-types error when "expanded" is missing', () => {
|
|
42
|
+
shallow( /*#__PURE__*/React.createElement(CustomDataProvider, {
|
|
43
|
+
data: {}
|
|
44
|
+
}, /*#__PURE__*/React.createElement(OrganisationUnitTree, {
|
|
45
|
+
roots: "/A001",
|
|
46
|
+
onChange: () => {},
|
|
47
|
+
handleCollapse: () => {},
|
|
48
|
+
handleExpand: () => {}
|
|
49
|
+
})));
|
|
50
|
+
expect(errorMock).toHaveBeenCalledTimes(1);
|
|
51
|
+
expect(errorMock.mock.calls[0][0]).toMatch(/^Warning: Failed prop type: Invalid prop `expanded` supplied to `OrganisationUnitTree`/);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
package/build/es/organisation-unit-tree/use-expanded/{helpers.js → create-expand-handlers.js}
RENAMED
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @param {string} path
|
|
3
|
-
* @returns {string[]}
|
|
4
|
-
*/
|
|
5
|
-
export const extractAllPathsFromPath = path => {
|
|
6
|
-
// remove leading slash and split by path delimiter/slashes
|
|
7
|
-
const segments = path.replace(/^\//, '').split('/');
|
|
8
|
-
const withSubPaths = segments.map((segment, index) => {
|
|
9
|
-
// take all segments from 0 to index and join them with the delimiter
|
|
10
|
-
return `/${segments.slice(0, index + 1).join('/')}`;
|
|
11
|
-
});
|
|
12
|
-
return withSubPaths;
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* @param {string[]} initiallyExpanded
|
|
16
|
-
* @returns {string[]}
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
export const getInitiallyExpandedPaths = initiallyExpanded => initiallyExpanded.reduce((all, curPath) => {
|
|
20
|
-
const allPathsInCurPath = extractAllPathsFromPath(curPath);
|
|
21
|
-
return [...all, ...allPathsInCurPath];
|
|
22
|
-
}, []);
|
|
23
1
|
/**
|
|
24
2
|
* @param {Object} args
|
|
25
3
|
* @param {string[]} args.expanded
|
|
@@ -28,7 +6,6 @@ export const getInitiallyExpandedPaths = initiallyExpanded => initiallyExpanded.
|
|
|
28
6
|
* @param {Function} [args.onCollapse]
|
|
29
7
|
* @returns {{ handleExpand: Function, handleCollapse: Function }}
|
|
30
8
|
*/
|
|
31
|
-
|
|
32
9
|
export const createExpandHandlers = ({
|
|
33
10
|
expanded,
|
|
34
11
|
setExpanded,
|
|
@@ -1,17 +1,4 @@
|
|
|
1
|
-
import { createExpandHandlers
|
|
2
|
-
describe('OrganisationUnitTree - useExpanded - getInitiallyExpandedPaths', () => {
|
|
3
|
-
const initiallyExpanded = ['/foo/bar/baz', '/foobar/barbaz/bazfoo'];
|
|
4
|
-
it('should include all initiallyExpanded paths in the returned expanded array', () => {
|
|
5
|
-
const actual = getInitiallyExpandedPaths(initiallyExpanded);
|
|
6
|
-
const expected = expect.arrayContaining(initiallyExpanded);
|
|
7
|
-
expect(actual).toEqual(expected);
|
|
8
|
-
});
|
|
9
|
-
it('should include all sub paths of the paths in initiallyExpanded in the returned exanded array', () => {
|
|
10
|
-
const actual = getInitiallyExpandedPaths(initiallyExpanded);
|
|
11
|
-
const expected = expect.arrayContaining(['/foo', '/foo/bar', '/foobar', '/foobar/barbaz']);
|
|
12
|
-
expect(actual).toEqual(expected);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
1
|
+
import { createExpandHandlers } from './create-expand-handlers.js';
|
|
15
2
|
describe('OrganisationUnitTree - useExpanded - createExpandHandlers', () => {
|
|
16
3
|
const initiallyExpanded = ['/foo/bar/baz', '/foobar/barbaz/bazfoo'];
|
|
17
4
|
const onExpand = jest.fn();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { getAllExpandedPaths } from '../../get-all-expanded-paths/index.js';
|
|
3
|
+
import { createExpandHandlers } from './create-expand-handlers.js';
|
|
3
4
|
/**
|
|
4
5
|
* @param {string[]} initiallyExpanded
|
|
5
6
|
* @param {Function} [onExpand]
|
|
@@ -7,9 +8,26 @@ import { getInitiallyExpandedPaths, createExpandHandlers } from './helpers.js';
|
|
|
7
8
|
* @returns {{ expanded: string[], handleExpand: Function, handleCollapse: Function }}
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
|
-
export const useExpanded = (
|
|
11
|
-
|
|
11
|
+
export const useExpanded = ({
|
|
12
|
+
initiallyExpanded,
|
|
13
|
+
onExpand,
|
|
14
|
+
onCollapse,
|
|
15
|
+
expandedControlled,
|
|
16
|
+
handleExpandControlled,
|
|
17
|
+
handleCollapseControlled
|
|
18
|
+
}) => {
|
|
19
|
+
const isControlled = !!expandedControlled;
|
|
20
|
+
const allInitiallyExpandedPaths = isControlled ? [] : getAllExpandedPaths(initiallyExpanded);
|
|
12
21
|
const [expanded, setExpanded] = useState(allInitiallyExpandedPaths);
|
|
22
|
+
|
|
23
|
+
if (isControlled) {
|
|
24
|
+
return {
|
|
25
|
+
expanded: expandedControlled,
|
|
26
|
+
handleExpand: handleExpandControlled,
|
|
27
|
+
handleCollapse: handleCollapseControlled
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
const {
|
|
14
32
|
handleExpand,
|
|
15
33
|
handleCollapse
|
|
@@ -1,31 +1,64 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { getAllExpandedPaths } from '../../get-all-expanded-paths/index.js';
|
|
3
|
+
import { createExpandHandlers } from './create-expand-handlers.js';
|
|
3
4
|
import { useExpanded } from './use-expanded.js';
|
|
4
5
|
jest.mock('react', () => ({
|
|
5
6
|
useState: jest.fn(initialValue => [initialValue, () => null])
|
|
6
7
|
}));
|
|
7
|
-
jest.mock('
|
|
8
|
-
|
|
8
|
+
jest.mock('../../get-all-expanded-paths/index.js', () => ({
|
|
9
|
+
getAllExpandedPaths: jest.fn(input => input)
|
|
10
|
+
}));
|
|
11
|
+
jest.mock('./create-expand-handlers.js', () => ({
|
|
9
12
|
createExpandHandlers: jest.fn(() => ({
|
|
10
13
|
handleCollapse: () => null,
|
|
11
14
|
handleExpand: () => null
|
|
12
15
|
}))
|
|
13
16
|
}));
|
|
14
17
|
describe('OrganisationUnitTree - useExpanded hook', () => {
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
const onExpand = jest.fn();
|
|
19
|
+
const onCollapse = jest.fn();
|
|
20
|
+
it('should use the getAllExpandedPaths helper to determine the initial state', () => {
|
|
21
|
+
getAllExpandedPaths.mockImplementationOnce(input => [...input, '/foo/bar/baz']);
|
|
17
22
|
const expected = ['/foo', '/foo/bar', '/foo/bar/baz'];
|
|
18
23
|
const {
|
|
19
24
|
expanded: actual
|
|
20
|
-
} = useExpanded(
|
|
25
|
+
} = useExpanded({
|
|
26
|
+
initiallyExpanded: ['/foo', '/foo/bar'],
|
|
27
|
+
onExpand,
|
|
28
|
+
onCollapse
|
|
29
|
+
});
|
|
21
30
|
expect(actual).toEqual(expected);
|
|
22
31
|
});
|
|
23
32
|
it('should pass the setExpanded function from seState to createExpandHandlers', () => {
|
|
24
33
|
const setExpanded = jest.fn();
|
|
25
34
|
useState.mockImplementationOnce(() => [[], setExpanded]);
|
|
26
|
-
useExpanded(
|
|
35
|
+
useExpanded({
|
|
36
|
+
initiallyExpanded: [],
|
|
37
|
+
onExpand,
|
|
38
|
+
onCollapse
|
|
39
|
+
});
|
|
27
40
|
expect(createExpandHandlers).toHaveBeenCalledWith(expect.objectContaining({
|
|
28
41
|
setExpanded
|
|
29
42
|
}));
|
|
30
43
|
});
|
|
44
|
+
it('should return the controlled values if all of them are being provided', () => {
|
|
45
|
+
const expandedControlled = ['/foo'];
|
|
46
|
+
const handleExpandControlled = jest.fn();
|
|
47
|
+
const handleCollapseControlled = jest.fn();
|
|
48
|
+
const {
|
|
49
|
+
expanded,
|
|
50
|
+
handleExpand,
|
|
51
|
+
handleCollapse
|
|
52
|
+
} = useExpanded({
|
|
53
|
+
initiallyExpanded: [],
|
|
54
|
+
onExpand,
|
|
55
|
+
onCollapse,
|
|
56
|
+
expandedControlled,
|
|
57
|
+
handleExpandControlled,
|
|
58
|
+
handleCollapseControlled
|
|
59
|
+
});
|
|
60
|
+
expect(expanded).toBe(expandedControlled);
|
|
61
|
+
expect(handleExpand).toBe(handleExpandControlled);
|
|
62
|
+
expect(handleCollapse).toBe(handleCollapseControlled);
|
|
63
|
+
});
|
|
31
64
|
});
|