@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.
Files changed (85) hide show
  1. package/build/cjs/__e2e__/common.js +134 -92
  2. package/build/cjs/__e2e__/controlled_expanded.stories.e2e.js +90 -0
  3. package/build/cjs/__e2e__/path_based_filtering.stories.e2e.js +0 -1
  4. package/build/cjs/__e2e__/single_selection.stories.e2e.js +4 -4
  5. package/build/cjs/__e2e__/tree_api.stories.e2e.js +6 -8
  6. package/build/cjs/__stories__/collapsed.js +22 -0
  7. package/build/cjs/__stories__/custom-expanded-imperative-open.js +184 -0
  8. package/build/cjs/__stories__/custom-node-label.js +33 -0
  9. package/build/cjs/__stories__/development-stories.js +96 -0
  10. package/build/cjs/__stories__/expanded.js +23 -0
  11. package/build/cjs/__stories__/filtered-root.js +25 -0
  12. package/build/cjs/__stories__/filtered.js +24 -0
  13. package/build/cjs/__stories__/force-reload-all.js +58 -0
  14. package/build/cjs/__stories__/force-reload-one-unit.js +37 -0
  15. package/build/cjs/__stories__/highlighted.js +24 -0
  16. package/build/cjs/__stories__/indeterminate.js +24 -0
  17. package/build/cjs/__stories__/loading-error-grandchild.js +44 -0
  18. package/build/cjs/__stories__/loading.js +39 -0
  19. package/build/cjs/__stories__/multiple-roots.js +24 -0
  20. package/build/cjs/__stories__/no-selection.js +26 -0
  21. package/build/cjs/__stories__/replace-roots.js +28 -0
  22. package/build/cjs/__stories__/root-error.js +48 -0
  23. package/build/cjs/__stories__/root-loading.js +48 -0
  24. package/build/cjs/__stories__/selected-multiple.js +25 -0
  25. package/build/cjs/__stories__/shared.js +140 -0
  26. package/build/cjs/__stories__/single-selection.js +26 -0
  27. package/build/cjs/features/controlled_expanded/index.js +60 -0
  28. package/build/cjs/features/controlled_expanded.feature +11 -0
  29. package/build/cjs/features/tree_api/index.js +1 -3
  30. package/build/cjs/get-all-expanded-paths/get-all-expanded-paths.js +40 -0
  31. package/build/cjs/get-all-expanded-paths/get-all-expanded-paths.test.js +17 -0
  32. package/build/cjs/get-all-expanded-paths/index.js +13 -0
  33. package/build/cjs/index.js +9 -1
  34. package/build/cjs/organisation-unit-node/use-open-state.js +11 -16
  35. package/build/cjs/organisation-unit-node/use-open-state.test.js +59 -101
  36. package/build/cjs/organisation-unit-tree/organisation-unit-tree.js +35 -20
  37. package/build/cjs/organisation-unit-tree/organisation-unit-tree.test.js +63 -0
  38. package/build/cjs/organisation-unit-tree/use-expanded/{helpers.js → create-expand-handlers.js} +1 -30
  39. package/build/cjs/organisation-unit-tree/use-expanded/{helpers.test.js → create-expand-handlers.test.js} +5 -18
  40. package/build/cjs/organisation-unit-tree/use-expanded/use-expanded.js +23 -4
  41. package/build/cjs/organisation-unit-tree/use-expanded/use-expanded.test.js +42 -8
  42. package/build/cjs/organisation-unit-tree.stories.js +191 -534
  43. package/build/es/__e2e__/common.js +121 -90
  44. package/build/es/__e2e__/controlled_expanded.stories.e2e.js +68 -0
  45. package/build/es/__e2e__/path_based_filtering.stories.e2e.js +0 -1
  46. package/build/es/__e2e__/single_selection.stories.e2e.js +4 -4
  47. package/build/es/__e2e__/tree_api.stories.e2e.js +6 -7
  48. package/build/es/__stories__/collapsed.js +8 -0
  49. package/build/es/__stories__/custom-expanded-imperative-open.js +166 -0
  50. package/build/es/__stories__/custom-node-label.js +19 -0
  51. package/build/es/__stories__/development-stories.js +73 -0
  52. package/build/es/__stories__/expanded.js +9 -0
  53. package/build/es/__stories__/filtered-root.js +11 -0
  54. package/build/es/__stories__/filtered.js +10 -0
  55. package/build/es/__stories__/force-reload-all.js +38 -0
  56. package/build/es/__stories__/force-reload-one-unit.js +25 -0
  57. package/build/es/__stories__/highlighted.js +10 -0
  58. package/build/es/__stories__/indeterminate.js +10 -0
  59. package/build/es/__stories__/loading-error-grandchild.js +29 -0
  60. package/build/es/__stories__/loading.js +24 -0
  61. package/build/es/__stories__/multiple-roots.js +10 -0
  62. package/build/es/__stories__/no-selection.js +12 -0
  63. package/build/es/__stories__/replace-roots.js +16 -0
  64. package/build/es/__stories__/root-error.js +33 -0
  65. package/build/es/__stories__/root-loading.js +33 -0
  66. package/build/es/__stories__/selected-multiple.js +11 -0
  67. package/build/es/__stories__/shared.js +120 -0
  68. package/build/es/__stories__/single-selection.js +12 -0
  69. package/build/es/features/controlled_expanded/index.js +57 -0
  70. package/build/es/features/controlled_expanded.feature +11 -0
  71. package/build/es/features/tree_api/index.js +1 -3
  72. package/build/es/get-all-expanded-paths/get-all-expanded-paths.js +31 -0
  73. package/build/es/get-all-expanded-paths/get-all-expanded-paths.test.js +14 -0
  74. package/build/es/get-all-expanded-paths/index.js +1 -0
  75. package/build/es/index.js +2 -1
  76. package/build/es/organisation-unit-node/use-open-state.js +11 -16
  77. package/build/es/organisation-unit-node/use-open-state.test.js +59 -94
  78. package/build/es/organisation-unit-tree/organisation-unit-tree.js +15 -1
  79. package/build/es/organisation-unit-tree/organisation-unit-tree.test.js +55 -0
  80. package/build/es/organisation-unit-tree/use-expanded/{helpers.js → create-expand-handlers.js} +0 -23
  81. package/build/es/organisation-unit-tree/use-expanded/{helpers.test.js → create-expand-handlers.test.js} +1 -14
  82. package/build/es/organisation-unit-tree/use-expanded/use-expanded.js +21 -3
  83. package/build/es/organisation-unit-tree/use-expanded/use-expanded.test.js +40 -7
  84. package/build/es/organisation-unit-tree.stories.js +23 -465
  85. package/package.json +5 -5
@@ -1,164 +1,129 @@
1
- import { useEffect, useState } from 'react';
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 not have an initial open state if the path is not in the expanded array', () => {
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
- open: actual
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 have an initial open state if the path is in the expanded array', () => {
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
- open: actual
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 setOpen with false when calling onToggleOpen if open is true', () => {
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
- onToggleOpen
59
- } = useOpenState({
44
+ result
45
+ } = renderHook(() => useOpenState({
60
46
  path,
61
- expanded
62
- });
63
- onToggleOpen();
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
- } = useOpenState({
82
- path: '/foo',
83
- expanded: []
84
- });
52
+ } = result.current;
85
53
  onToggleOpen();
86
- expect(setOpen).toHaveBeenCalledWith(!open);
54
+ expect(onCollapse).toHaveBeenCalledWith({
55
+ path: '/foo'
56
+ });
57
+ expect(onExpand).toHaveBeenCalledTimes(0);
87
58
  });
88
- it('should call the onExpand callback with the path when calling onToggleOpen and open was false', () => {
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
- onToggleOpen
93
- } = useOpenState({
63
+ result
64
+ } = renderHook(() => useOpenState({
94
65
  path,
95
- expanded: [],
96
- onExpand,
97
- onCollapse
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
- } = useOpenState({
111
- path,
112
- expanded: [],
113
- onExpand,
114
- onCollapse
115
- });
71
+ } = result.current;
116
72
  onToggleOpen();
117
- expect(onCollapse).toHaveBeenCalledWith({
118
- path
73
+ expect(onExpand).toHaveBeenCalledWith({
74
+ path: '/foo'
119
75
  });
120
- expect(onExpand).toHaveBeenCalledTimes(0);
76
+ expect(onCollapse).toHaveBeenCalledTimes(0);
121
77
  });
122
- it('should set the state to open if there is an error and autoExpandLoadingError is true', () => {
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
- open
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
- expect(open).toBe(true);
89
+ }));
90
+ const {
91
+ openedOnceDueToError
92
+ } = result.current;
93
+ expect(openedOnceDueToError).toBe(true);
135
94
  });
136
- it('should not alter the state if there is an error but autoExpandLoadingError is falsy', () => {
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
- open
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 alter the state if autoExpandLoadingError is true but there is no error', () => {
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
- open
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(initiallyExpanded, onExpand, onCollapse);
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
+ });
@@ -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, getInitiallyExpandedPaths } from './helpers.js';
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 { getInitiallyExpandedPaths, createExpandHandlers } from './helpers.js';
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 = (initiallyExpanded, onExpand, onCollapse) => {
11
- const allInitiallyExpandedPaths = getInitiallyExpandedPaths(initiallyExpanded);
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 { createExpandHandlers, getInitiallyExpandedPaths } from './helpers.js';
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('../use-expanded/helpers.js', () => ({
8
- getInitiallyExpandedPaths: jest.fn(input => input),
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
- it('should use the getInitiallyExpandedPaths helper to determine the initial state', () => {
16
- getInitiallyExpandedPaths.mockImplementationOnce(input => [...input, '/foo/bar/baz']);
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(['/foo', '/foo/bar']);
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
  });