@dhis2-ui/organisation-unit-tree 10.16.2 → 10.16.3
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/package.json +8 -7
- package/src/__e2e__/children_as_child_nodes.js +23 -0
- package/src/__e2e__/common.js +70 -0
- package/src/__e2e__/controlled_expanded.js +89 -0
- package/src/__e2e__/displaying_loading_error.js +45 -0
- package/src/__e2e__/expanded.js +42 -0
- package/src/__e2e__/force_reload.js +66 -0
- package/src/__e2e__/get-organisation-unit-data.js +119 -0
- package/src/__e2e__/highlight.js +23 -0
- package/src/__e2e__/loading_state.js +37 -0
- package/src/__e2e__/multi_selection.js +24 -0
- package/src/__e2e__/namespace.js +1 -0
- package/src/__e2e__/no_selection.js +32 -0
- package/src/__e2e__/path_based_filtering.js +49 -0
- package/src/__e2e__/single_selection.js +46 -0
- package/src/__e2e__/sub_unit_as_root.js +28 -0
- package/src/__e2e__/tree_api.js +55 -0
- package/src/__stories__/collapsed.js +11 -0
- package/src/__stories__/custom-expanded-imperative-open.js +181 -0
- package/src/__stories__/custom-node-label.js +19 -0
- package/src/__stories__/development-stories.js +86 -0
- package/src/__stories__/expanded.js +12 -0
- package/src/__stories__/filtered-root.js +15 -0
- package/src/__stories__/filtered.js +13 -0
- package/src/__stories__/force-reload-all.js +46 -0
- package/src/__stories__/force-reload-one-unit.js +36 -0
- package/src/__stories__/highlighted.js +13 -0
- package/src/__stories__/indeterminate.js +13 -0
- package/src/__stories__/loading-error-grandchild.js +39 -0
- package/src/__stories__/loading.js +27 -0
- package/src/__stories__/multiple-roots.js +20 -0
- package/src/__stories__/no-selection.js +16 -0
- package/src/__stories__/replace-roots.js +28 -0
- package/src/__stories__/root-error.js +36 -0
- package/src/__stories__/root-loading.js +34 -0
- package/src/__stories__/rtl.js +14 -0
- package/src/__stories__/selected-multiple.js +18 -0
- package/src/__stories__/shared.js +192 -0
- package/src/__stories__/single-selection.js +16 -0
- package/src/features/children_as_child_nodes/index.js +29 -0
- package/src/features/children_as_child_nodes.feature +6 -0
- package/src/features/controlled_expanded/index.js +86 -0
- package/src/features/controlled_expanded.feature +11 -0
- package/src/features/displaying_loading_error/index.js +46 -0
- package/src/features/displaying_loading_error.feature +24 -0
- package/src/features/expanded/index.js +87 -0
- package/src/features/expanded.feature +27 -0
- package/src/features/force_reload/index.js +36 -0
- package/src/features/force_reload.feature +7 -0
- package/src/features/highlight/index.js +9 -0
- package/src/features/highlight.feature +5 -0
- package/src/features/loading_state/index.js +26 -0
- package/src/features/loading_state.feature +7 -0
- package/src/features/multi_selection/index.js +94 -0
- package/src/features/multi_selection.feature +31 -0
- package/src/features/no_selection/index.js +41 -0
- package/src/features/no_selection.feature +13 -0
- package/src/features/path_based_filtering/index.js +97 -0
- package/src/features/path_based_filtering.feature +24 -0
- package/src/features/single_selection/index.js +41 -0
- package/src/features/single_selection.feature +20 -0
- package/src/features/sub_unit_as_root/index.js +83 -0
- package/src/features/sub_unit_as_root.feature +34 -0
- package/src/features/tree_api/index.js +121 -0
- package/src/features/tree_api.feature +37 -0
- package/src/get-all-expanded-paths/get-all-expanded-paths.js +32 -0
- package/src/get-all-expanded-paths/get-all-expanded-paths.test.js +22 -0
- package/src/get-all-expanded-paths/index.js +1 -0
- package/src/helpers/index.js +3 -0
- package/src/helpers/is-path-included.js +15 -0
- package/src/helpers/left-trim-to-root-id.js +3 -0
- package/src/helpers/sort-node-children-alphabetically.js +5 -0
- package/src/index.js +6 -0
- package/src/locales/ar/translations.json +5 -0
- package/src/locales/cs/translations.json +5 -0
- package/src/locales/en/translations.json +5 -0
- package/src/locales/es/translations.json +5 -0
- package/src/locales/es_419/translations.json +5 -0
- package/src/locales/fr/translations.json +5 -0
- package/src/locales/index.js +50 -0
- package/src/locales/lo/translations.json +5 -0
- package/src/locales/nb/translations.json +5 -0
- package/src/locales/nl/translations.json +5 -0
- package/src/locales/pt/translations.json +5 -0
- package/src/locales/ru/translations.json +5 -0
- package/src/locales/uk/translations.json +5 -0
- package/src/locales/uz_Latn/translations.json +5 -0
- package/src/locales/uz_UZ_Cyrl/translations.json +5 -0
- package/src/locales/uz_UZ_Latn/translations.json +5 -0
- package/src/locales/vi/translations.json +5 -0
- package/src/locales/zh/translations.json +5 -0
- package/src/locales/zh_CN/translations.json +5 -0
- package/src/organisation-unit-node/compute-child-nodes.js +27 -0
- package/src/organisation-unit-node/compute-child-nodes.test.js +85 -0
- package/src/organisation-unit-node/error-message.js +23 -0
- package/src/organisation-unit-node/has-descendant-selected-paths.js +15 -0
- package/src/organisation-unit-node/has-descendant-selected-paths.test.js +30 -0
- package/src/organisation-unit-node/index.js +1 -0
- package/src/organisation-unit-node/label/disabled-selection-label.js +26 -0
- package/src/organisation-unit-node/label/icon-empty.js +31 -0
- package/src/organisation-unit-node/label/icon-folder-closed.js +38 -0
- package/src/organisation-unit-node/label/icon-folder-open.js +49 -0
- package/src/organisation-unit-node/label/icon-single.js +41 -0
- package/src/organisation-unit-node/label/icon.js +35 -0
- package/src/organisation-unit-node/label/iconized-checkbox.js +67 -0
- package/src/organisation-unit-node/label/index.js +1 -0
- package/src/organisation-unit-node/label/label-container.js +36 -0
- package/src/organisation-unit-node/label/label.js +146 -0
- package/src/organisation-unit-node/label/single-selection-label.js +60 -0
- package/src/organisation-unit-node/loading-spinner.js +22 -0
- package/src/organisation-unit-node/organisation-unit-node-children.js +123 -0
- package/src/organisation-unit-node/organisation-unit-node.js +190 -0
- package/src/organisation-unit-node/use-open-state.js +37 -0
- package/src/organisation-unit-node/use-open-state.test.js +111 -0
- package/src/organisation-unit-node/use-org-children.js +63 -0
- package/src/organisation-unit-node/use-org-children.test.js +314 -0
- package/src/organisation-unit-node/use-org-data/index.js +1 -0
- package/src/organisation-unit-node/use-org-data/use-org-data.js +40 -0
- package/src/organisation-unit-node/use-org-data/use-org-data.test.js +137 -0
- package/src/organisation-unit-tree/default-render-node-label/default-render-node-label.js +1 -0
- package/src/organisation-unit-tree/default-render-node-label/index.js +1 -0
- package/src/organisation-unit-tree/filter-root-ids.js +9 -0
- package/src/organisation-unit-tree/index.js +3 -0
- package/src/organisation-unit-tree/organisation-unit-tree-root-error.js +20 -0
- package/src/organisation-unit-tree/organisation-unit-tree-root-loading.js +22 -0
- package/src/organisation-unit-tree/organisation-unit-tree.js +253 -0
- package/src/organisation-unit-tree/organisation-unit-tree.test.js +77 -0
- package/src/organisation-unit-tree/use-expanded/create-expand-handlers.js +45 -0
- package/src/organisation-unit-tree/use-expanded/create-expand-handlers.test.js +54 -0
- package/src/organisation-unit-tree/use-expanded/index.js +1 -0
- package/src/organisation-unit-tree/use-expanded/use-expanded.js +42 -0
- package/src/organisation-unit-tree/use-expanded/use-expanded.test.js +73 -0
- package/src/organisation-unit-tree/use-force-reload.js +22 -0
- package/src/organisation-unit-tree/use-force-reload.test.js +43 -0
- package/src/organisation-unit-tree/use-root-org-data/index.js +1 -0
- package/src/organisation-unit-tree/use-root-org-data/patch-missing-display-name.js +20 -0
- package/src/organisation-unit-tree/use-root-org-data/patch-missing-display-name.test.js +24 -0
- package/src/organisation-unit-tree/use-root-org-data/use-root-org-data.js +60 -0
- package/src/organisation-unit-tree/use-root-org-data/use-root-org-unit.test.js +184 -0
- package/src/organisation-unit-tree.e2e.stories.js +28 -0
- package/src/organisation-unit-tree.prod.stories.js +70 -0
- package/src/prop-types.js +33 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { OrganisationUnitTree } from '../index.js'
|
|
3
|
+
import { onChange } from './shared.js'
|
|
4
|
+
|
|
5
|
+
export const NoSelection = () => (
|
|
6
|
+
<OrganisationUnitTree
|
|
7
|
+
onChange={onChange}
|
|
8
|
+
disableSelection
|
|
9
|
+
name="Root org unit"
|
|
10
|
+
roots={['A0000000000']}
|
|
11
|
+
selected={['/A0000000000/A0000000001']}
|
|
12
|
+
initiallyExpanded={['/A0000000000']}
|
|
13
|
+
/>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
NoSelection.storyName = 'No selection'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
export const ReplaceRoots = () => {
|
|
4
|
+
return (
|
|
5
|
+
<p>
|
|
6
|
+
This is currently not working due to limitations of the data engine
|
|
7
|
+
in the app runtime. Normally the root unit would've been
|
|
8
|
+
replaced after 1000 milliseconds.
|
|
9
|
+
</p>
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
//const [roots, setRoots] = useState(['A0000000000'])
|
|
13
|
+
|
|
14
|
+
//useEffect(() => {
|
|
15
|
+
// setTimeout(() => setRoots(['A0000000001']), delay)
|
|
16
|
+
//}, [])
|
|
17
|
+
|
|
18
|
+
//return (
|
|
19
|
+
// <OrganisationUnitTree
|
|
20
|
+
// name="Root org unit"
|
|
21
|
+
// roots={roots}
|
|
22
|
+
// onChange={console.log.bind(null, 'onChange')}
|
|
23
|
+
// initiallyExpanded={['/A0000000001']}
|
|
24
|
+
// />
|
|
25
|
+
//)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
ReplaceRoots.storyName = 'Replace roots'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { CustomDataProvider } from '@dhis2/app-runtime'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { OrganisationUnitTree } from '../index.js'
|
|
4
|
+
import { customData, onChange } from './shared.js'
|
|
5
|
+
|
|
6
|
+
export const RootError = () => (
|
|
7
|
+
<CustomDataProvider
|
|
8
|
+
data={{
|
|
9
|
+
...customData,
|
|
10
|
+
organisationUnits: (...args) => {
|
|
11
|
+
const [, { id }] = args
|
|
12
|
+
if (id === 'A0000000000') {
|
|
13
|
+
return Promise.reject(
|
|
14
|
+
'This is a custom error message, it could be anything'
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return customData.organisationUnits(...args)
|
|
19
|
+
},
|
|
20
|
+
}}
|
|
21
|
+
>
|
|
22
|
+
<fieldset style={{ maxWidth: 600 }}>
|
|
23
|
+
<legend style={{ padding: '0 10px' }}>
|
|
24
|
+
Custom container (max-width: 600px)
|
|
25
|
+
</legend>
|
|
26
|
+
<OrganisationUnitTree
|
|
27
|
+
onChange={onChange}
|
|
28
|
+
name="Root org unit"
|
|
29
|
+
roots={['A0000000000']}
|
|
30
|
+
initiallyExpanded={['/A0000000000/A0000000001']}
|
|
31
|
+
/>
|
|
32
|
+
</fieldset>
|
|
33
|
+
</CustomDataProvider>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
RootError.storyName = 'Root error'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { CustomDataProvider } from '@dhis2/app-runtime'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { OrganisationUnitTree } from '../index.js'
|
|
4
|
+
import { customData, onChange } from './shared.js'
|
|
5
|
+
|
|
6
|
+
export const RootLoading = () => (
|
|
7
|
+
<CustomDataProvider
|
|
8
|
+
data={{
|
|
9
|
+
...customData,
|
|
10
|
+
organisationUnits: (...args) => {
|
|
11
|
+
const [, { id }] = args
|
|
12
|
+
if (id === 'A0000000000') {
|
|
13
|
+
return new Promise(() => null)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return customData.organisationUnits(...args)
|
|
17
|
+
},
|
|
18
|
+
}}
|
|
19
|
+
>
|
|
20
|
+
<fieldset style={{ maxWidth: 600 }}>
|
|
21
|
+
<legend style={{ padding: '0 10px' }}>
|
|
22
|
+
Custom container (max-width: 600px)
|
|
23
|
+
</legend>
|
|
24
|
+
<OrganisationUnitTree
|
|
25
|
+
onChange={onChange}
|
|
26
|
+
name="Root org unit"
|
|
27
|
+
roots={['A0000000000']}
|
|
28
|
+
initiallyExpanded={['/A0000000000/A0000000001']}
|
|
29
|
+
/>
|
|
30
|
+
</fieldset>
|
|
31
|
+
</CustomDataProvider>
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
RootLoading.storyName = 'Root loading'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { OrganisationUnitTree } from '../index.js'
|
|
3
|
+
import { onChange } from './shared.js'
|
|
4
|
+
|
|
5
|
+
export const RTL = () => (
|
|
6
|
+
<div dir="rtl">
|
|
7
|
+
<OrganisationUnitTree
|
|
8
|
+
onChange={onChange}
|
|
9
|
+
name="Root org unit"
|
|
10
|
+
roots={['A0000000000']}
|
|
11
|
+
initiallyExpanded={['/A0000000000/A0000000001']}
|
|
12
|
+
/>
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { OrganisationUnitTree } from '../index.js'
|
|
3
|
+
import { onChange } from './shared.js'
|
|
4
|
+
|
|
5
|
+
export const SelectedMultiple = () => (
|
|
6
|
+
<OrganisationUnitTree
|
|
7
|
+
onChange={onChange}
|
|
8
|
+
name="Root org unit"
|
|
9
|
+
roots={['A0000000000']}
|
|
10
|
+
selected={[
|
|
11
|
+
'/A0000000000/A0000000002',
|
|
12
|
+
'/A0000000000/A0000000001/A0000000003',
|
|
13
|
+
]}
|
|
14
|
+
initiallyExpanded={['/A0000000000', '/A0000000000/A0000000001']}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
SelectedMultiple.storyName = 'Selected multiple'
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { CustomDataProvider } from '@dhis2/app-runtime'
|
|
2
|
+
import PropTypes from 'prop-types'
|
|
3
|
+
import React, { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export const log = true
|
|
6
|
+
export const onChange = (...args) => log && console.log('onChange', ...args)
|
|
7
|
+
export const onExpand = (...args) => log && console.log('onExpand', ...args)
|
|
8
|
+
export const onCollapse = (...args) => log && console.log('onCollapse', ...args)
|
|
9
|
+
export const onChildrenLoaded = (...args) =>
|
|
10
|
+
log && console.log('onChildrenLoaded', ...args)
|
|
11
|
+
|
|
12
|
+
const createResponse = ({ fields, id, path, displayName, children }) => ({
|
|
13
|
+
...(fields.includes('id') ? { id } : {}),
|
|
14
|
+
...(fields.includes('path') ? { path } : {}),
|
|
15
|
+
...(fields.includes('displayName') ? { displayName } : {}),
|
|
16
|
+
...(fields.includes('children::size') ? { children: children.length } : {}),
|
|
17
|
+
...(fields.includes('children[id,path,displayName]') ? { children } : {}),
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
export const getOrganisationUnitData = (id, { fields }) => {
|
|
21
|
+
let data
|
|
22
|
+
|
|
23
|
+
if (id === 'A0000000000') {
|
|
24
|
+
data = createResponse({
|
|
25
|
+
fields,
|
|
26
|
+
id: 'A0000000000',
|
|
27
|
+
path: '/A0000000000',
|
|
28
|
+
displayName: 'Org Unit 1',
|
|
29
|
+
children: [
|
|
30
|
+
{
|
|
31
|
+
id: 'A0000000001',
|
|
32
|
+
path: '/A0000000000/A0000000001',
|
|
33
|
+
children: [{ id: 'A0000000003' }, { id: 'A0000000004' }],
|
|
34
|
+
displayName: 'Org Unit 2',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'A0000000002',
|
|
38
|
+
path: '/A0000000000/A0000000002',
|
|
39
|
+
children: [],
|
|
40
|
+
displayName: 'Org Unit 3',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'A0000000006',
|
|
44
|
+
path: '/A0000000000/A0000000006',
|
|
45
|
+
children: [],
|
|
46
|
+
displayName: 'Org Unit 7',
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (id === 'A0000000001') {
|
|
53
|
+
data = createResponse({
|
|
54
|
+
fields,
|
|
55
|
+
id: 'A0000000001',
|
|
56
|
+
path: '/A0000000000/A0000000001',
|
|
57
|
+
displayName: 'Org Unit 2',
|
|
58
|
+
children: [
|
|
59
|
+
{
|
|
60
|
+
id: 'A0000000003',
|
|
61
|
+
path: '/A0000000000/A0000000001/A0000000003',
|
|
62
|
+
children: [],
|
|
63
|
+
displayName: 'Org Unit 4',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'A0000000004',
|
|
67
|
+
path: '/A0000000000/A0000000001/A0000000004',
|
|
68
|
+
children: [],
|
|
69
|
+
displayName: 'Org Unit 5',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (id === 'A0000000002') {
|
|
76
|
+
data = createResponse({
|
|
77
|
+
fields,
|
|
78
|
+
displayName: 'Org Unit 3',
|
|
79
|
+
id: 'A0000000002',
|
|
80
|
+
path: '/A0000000000/A0000000002',
|
|
81
|
+
children: [],
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (id === 'A0000000003') {
|
|
86
|
+
data = createResponse({
|
|
87
|
+
fields,
|
|
88
|
+
displayName: 'Org Unit 4',
|
|
89
|
+
id: 'A0000000003',
|
|
90
|
+
path: '/A0000000000/A0000000001/A0000000003',
|
|
91
|
+
children: [
|
|
92
|
+
{
|
|
93
|
+
id: 'A0000000007',
|
|
94
|
+
path: '/A0000000000/A0000000001/A0000000003/A0000000007',
|
|
95
|
+
children: [],
|
|
96
|
+
displayName: 'Org Unit 8',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (id === 'A0000000004') {
|
|
103
|
+
data = createResponse({
|
|
104
|
+
fields,
|
|
105
|
+
displayName: 'Org Unit 5',
|
|
106
|
+
id: 'A0000000004',
|
|
107
|
+
path: '/A0000000000/A0000000001/A0000000004',
|
|
108
|
+
children: [],
|
|
109
|
+
})
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (id === 'A0000000006') {
|
|
113
|
+
data = createResponse({
|
|
114
|
+
fields,
|
|
115
|
+
displayName: 'Org Unit 7',
|
|
116
|
+
id: 'A0000000006',
|
|
117
|
+
path: '/A0000000000/A0000000006',
|
|
118
|
+
children: [],
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (id === 'A0000000007') {
|
|
123
|
+
data = createResponse({
|
|
124
|
+
fields,
|
|
125
|
+
displayName: 'Org Unit 8',
|
|
126
|
+
id: 'A0000000007',
|
|
127
|
+
path: '/A0000000000/A0000000001/A0000000003/A0000000007',
|
|
128
|
+
children: [],
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return data
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const customData = {
|
|
136
|
+
organisationUnits: (_, { id, params = {} }) => {
|
|
137
|
+
const data = getOrganisationUnitData(id, params)
|
|
138
|
+
|
|
139
|
+
if (!data) {
|
|
140
|
+
return Promise.reject(new Error('404 - Org unit not found'))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return Promise.resolve(data)
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const StatefulMultiSelectionWrapper = ({
|
|
148
|
+
children,
|
|
149
|
+
onSelectionChange = () => null,
|
|
150
|
+
}) => {
|
|
151
|
+
const [selected, setSelected] = useState([])
|
|
152
|
+
|
|
153
|
+
return children({
|
|
154
|
+
selected,
|
|
155
|
+
onChange: (...args) => {
|
|
156
|
+
onChange(...args)
|
|
157
|
+
|
|
158
|
+
const [{ selected: newSelected }] = args
|
|
159
|
+
setSelected(newSelected)
|
|
160
|
+
onSelectionChange(newSelected)
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
StatefulMultiSelectionWrapper.propTypes = {
|
|
166
|
+
children: PropTypes.func.isRequired,
|
|
167
|
+
onSelectionChange: PropTypes.func,
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export const createDecoratorStatefulMultiSelection = (args) => {
|
|
171
|
+
return (Story) => (
|
|
172
|
+
<StatefulMultiSelectionWrapper
|
|
173
|
+
onSelectionChange={args?.onSelectionChange}
|
|
174
|
+
>
|
|
175
|
+
{({ selected, onChange }) => (
|
|
176
|
+
<Story selected={selected} onChange={onChange} />
|
|
177
|
+
)}
|
|
178
|
+
</StatefulMultiSelectionWrapper>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const createDecoratorCustomDataProvider = (args) => {
|
|
183
|
+
const data = args?.data || customData
|
|
184
|
+
|
|
185
|
+
return (Story) => {
|
|
186
|
+
return (
|
|
187
|
+
<CustomDataProvider data={data}>
|
|
188
|
+
<Story />
|
|
189
|
+
</CustomDataProvider>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { OrganisationUnitTree } from '../index.js'
|
|
3
|
+
import { onChange } from './shared.js'
|
|
4
|
+
|
|
5
|
+
export const SingleSelection = () => (
|
|
6
|
+
<OrganisationUnitTree
|
|
7
|
+
onChange={onChange}
|
|
8
|
+
singleSelection
|
|
9
|
+
name="Root org unit"
|
|
10
|
+
roots={['A0000000000']}
|
|
11
|
+
selected={['/A0000000000/A0000000001']}
|
|
12
|
+
initiallyExpanded={['/A0000000000']}
|
|
13
|
+
/>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
SingleSelection.storyName = 'Single selection'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given('an OrganisationUnitTree with children is rendered', () => {
|
|
4
|
+
cy.visitStory('OrganisationUnitTree', 'Closed with children')
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
Given('the node is open', () => {
|
|
8
|
+
cy.get(
|
|
9
|
+
'[data-test="dhis2-uiwidgets-orgunittree"] > [data-test="dhis2-uiwidgets-orgunittree-node"]'
|
|
10
|
+
).as('rootUnit')
|
|
11
|
+
|
|
12
|
+
cy.get('@rootUnit').openOrgUnitNode()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
Then("its children are nodes inside the unit's node", () => {
|
|
16
|
+
cy.get('@rootUnit')
|
|
17
|
+
.find(
|
|
18
|
+
'> [data-test="dhis2-uiwidgets-orgunittree-node-content"] > [data-test="dhis2-uiwidgets-orgunittree-node-leaves"]'
|
|
19
|
+
)
|
|
20
|
+
.children()
|
|
21
|
+
.should('have.length', 3)
|
|
22
|
+
.and((children) =>
|
|
23
|
+
children.each((_, child) => {
|
|
24
|
+
const $child = Cypress.$(child)
|
|
25
|
+
const dataTest = $child.data('test')
|
|
26
|
+
expect(dataTest).to.equal('dhis2-uiwidgets-orgunittree-node')
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
import { getOrganisationUnitData } from '../../__e2e__/get-organisation-unit-data.js'
|
|
3
|
+
import { namespace } from '../../__e2e__/namespace.js'
|
|
4
|
+
|
|
5
|
+
const expectOrgUnitsToBeDisplayed = (ids) => {
|
|
6
|
+
const expandedLabels = ids.map(
|
|
7
|
+
(id) =>
|
|
8
|
+
getOrganisationUnitData(id, { fields: ['displayName'] }).displayName
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
expandedLabels.forEach((label) => {
|
|
12
|
+
cy.get(`:contains("${label}")`).should('exist')
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const expectOrgUnitsToNotBeDisplayed = (ids) => {
|
|
17
|
+
const expandedLabels = ids.map((id) => {
|
|
18
|
+
const data = getOrganisationUnitData(id, { fields: ['displayName'] })
|
|
19
|
+
return data.displayName
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
expandedLabels.forEach((label) => {
|
|
23
|
+
cy.get(`:contains("${label}")`).should('not.exist')
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Given(
|
|
28
|
+
'the initial state of the controlled expanded prop has some paths',
|
|
29
|
+
() => {
|
|
30
|
+
cy.visitStory(namespace, 'Controlled')
|
|
31
|
+
cy.get(':contains("Org Unit 1")').should('exist')
|
|
32
|
+
cy.window().then((win) => {
|
|
33
|
+
cy.wrap(win.initiallyExpandedPaths).as('providedPaths', {
|
|
34
|
+
type: 'static',
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
When('the org unit tree should is done loading the provided paths', () => {
|
|
41
|
+
cy.window().then((win) => {
|
|
42
|
+
const expandedIds = win.initiallyExpandedPaths.map(
|
|
43
|
+
(path) => path.match(/[^/]+$/)[0]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
expectOrgUnitsToBeDisplayed(expandedIds)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
When('the user clicks on a button to collapse one of the opened paths', () => {
|
|
51
|
+
cy.window().then((win) => {
|
|
52
|
+
cy.wrap([win.orgUnitPathToExpand]).as('providedPaths', {
|
|
53
|
+
type: 'static',
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
cy.get('[data-test="org-unit-toggle"]').click()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
Then(
|
|
61
|
+
'the org unit tree should open the provided paths when done loading',
|
|
62
|
+
() => {
|
|
63
|
+
cy.get('@providedPaths').then((providedPaths) => {
|
|
64
|
+
const providedIds = providedPaths.map(
|
|
65
|
+
(path) => path.match(/[^/]+$/)[0]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
expectOrgUnitsToBeDisplayed(providedIds)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
Then('the path should close', () => {
|
|
74
|
+
cy.get('@providedPaths').then((providedPaths) => {
|
|
75
|
+
const providedIds = providedPaths.map((path) => path.match(/[^/]+$/)[0])
|
|
76
|
+
const hiddenChildrenIds = providedIds.reduce((acc, cur) => {
|
|
77
|
+
const curData = getOrganisationUnitData(cur, {
|
|
78
|
+
fields: ['children[id,path,displayName]'],
|
|
79
|
+
})
|
|
80
|
+
const childrenIds = curData.children.map(({ id }) => id)
|
|
81
|
+
return [...acc, ...childrenIds]
|
|
82
|
+
}, [])
|
|
83
|
+
|
|
84
|
+
expectOrgUnitsToNotBeDisplayed(hiddenChildrenIds)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Feature: The expanded paths of the org unit tree can be controlled
|
|
2
|
+
|
|
3
|
+
Scenario: The org unit tree expands some paths initially
|
|
4
|
+
Given the initial state of the controlled expanded prop has some paths
|
|
5
|
+
Then the org unit tree should open the provided paths when done loading
|
|
6
|
+
|
|
7
|
+
Scenario: The user collapses a path by pressing a button
|
|
8
|
+
Given the initial state of the controlled expanded prop has some paths
|
|
9
|
+
When the org unit tree should is done loading the provided paths
|
|
10
|
+
When the user clicks on a button to collapse one of the opened paths
|
|
11
|
+
Then the path should close
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
Given(
|
|
4
|
+
"loading errors do not display automatically and loading A0000000001's children will fail",
|
|
5
|
+
() => {
|
|
6
|
+
cy.visitStory('OrganisationUnitTree', 'A 0000000001 loading error')
|
|
7
|
+
}
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
Given(
|
|
11
|
+
"loading errors display automatically and loading A0000000001's children will fail",
|
|
12
|
+
() => {
|
|
13
|
+
cy.visitStory(
|
|
14
|
+
'OrganisationUnitTree',
|
|
15
|
+
'A 0000000001 loading error autoexpand'
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
Given('the OrganisationUnitTree is closed', () => {
|
|
21
|
+
cy.get(
|
|
22
|
+
'[data-test="dhis2-uiwidgets-orgunittree"] > [data-test="dhis2-uiwidgets-orgunittree-node"]'
|
|
23
|
+
)
|
|
24
|
+
.as('rootNode')
|
|
25
|
+
.shouldBeAClosedNode()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
When('the A0000000000 path is opened', () => {
|
|
29
|
+
cy.getOrgUnitByLabel('Org Unit 1').openOrgUnitNode()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
When('the A0000000000 -> A0000000001 path is opened', () => {
|
|
33
|
+
cy.getOrgUnitByLabel('Org Unit 2').openOrgUnitNode()
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
Then('no error message is shown', () => {
|
|
37
|
+
cy.getOrgUnitByLabel('Org Unit 2')
|
|
38
|
+
.find('[data-test="dhis2-uiwidgets-orgunittree-error"]')
|
|
39
|
+
.should('not.exist')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
Then('an appropriate error message is shown', () => {
|
|
43
|
+
cy.getOrgUnitByLabel('Org Unit 2')
|
|
44
|
+
.find('[data-test="dhis2-uiwidgets-orgunittree-error"]')
|
|
45
|
+
.should('contain', 'Could not load children')
|
|
46
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Feature: When loading children fails a loading error should be shown
|
|
2
|
+
|
|
3
|
+
Due to the asynchronous nature of the tree, loading children
|
|
4
|
+
can fail at any given time. An appropriate error message should
|
|
5
|
+
be displayed when this happens
|
|
6
|
+
|
|
7
|
+
The error does not show automatically until a node's
|
|
8
|
+
children should be displayed.
|
|
9
|
+
This behavior can be changed to expand the node immediately
|
|
10
|
+
when the loading process fails.
|
|
11
|
+
|
|
12
|
+
Scenario: Loading the children of the root unit fails and error should not be auto expanded
|
|
13
|
+
Given loading errors do not display automatically and loading A0000000001's children will fail
|
|
14
|
+
And the OrganisationUnitTree is closed
|
|
15
|
+
When the A0000000000 path is opened
|
|
16
|
+
Then no error message is shown
|
|
17
|
+
When the A0000000000 -> A0000000001 path is opened
|
|
18
|
+
Then an appropriate error message is shown
|
|
19
|
+
|
|
20
|
+
Scenario: Loading the children of the root unit fails and error should be auto expanded
|
|
21
|
+
Given loading errors display automatically and loading A0000000001's children will fail
|
|
22
|
+
And the OrganisationUnitTree is closed
|
|
23
|
+
When the A0000000000 path is opened
|
|
24
|
+
Then an appropriate error message is shown
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
|
|
2
|
+
|
|
3
|
+
const getRootOrgUnitByLabel = (label) => {
|
|
4
|
+
const rootOrgUnitLabelSelector = `
|
|
5
|
+
[data-test="dhis2-uiwidgets-orgunittree"]
|
|
6
|
+
> [data-test="dhis2-uiwidgets-orgunittree-node"]
|
|
7
|
+
> [data-test="dhis2-uiwidgets-orgunittree-node-content"]
|
|
8
|
+
> [data-test="dhis2-uiwidgets-orgunittree-node-label"]
|
|
9
|
+
label
|
|
10
|
+
`
|
|
11
|
+
|
|
12
|
+
return cy
|
|
13
|
+
.contains(rootOrgUnitLabelSelector, label)
|
|
14
|
+
.parents('[data-test="dhis2-uiwidgets-orgunittree-node"]')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Given(
|
|
18
|
+
'a OrganisationUnitTree with children and no paths in the initiallyExpanded prop is rendered',
|
|
19
|
+
() => {
|
|
20
|
+
cy.visitStory('OrganisationUnitTree', 'No initially expanded paths')
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
Given(
|
|
25
|
+
'a OrganisationUnitTree with children and the path of the first unit on the second level in the initiallyExpanded prop is rendered',
|
|
26
|
+
() => {
|
|
27
|
+
cy.visitStory('OrganisationUnitTree', 'Initially expanded paths')
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
Given(
|
|
32
|
+
'both a sub org unit with children and a main org unit are root org units',
|
|
33
|
+
() => {
|
|
34
|
+
cy.visitStory(
|
|
35
|
+
'OrganisationUnitTree',
|
|
36
|
+
'with root main and root sub org unit'
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
Given('the root main org unit is expanded', () => {
|
|
42
|
+
getRootOrgUnitByLabel('Org Unit 1')
|
|
43
|
+
.find('> [data-test="dhis2-uiwidgets-orgunittree-node-toggle"]')
|
|
44
|
+
.click()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
When('the user expands the sub org unit within the main org unit tree', () => {
|
|
48
|
+
getRootOrgUnitByLabel('Org Unit 1')
|
|
49
|
+
.contains(
|
|
50
|
+
'[data-test="dhis2-uiwidgets-orgunittree-node"]',
|
|
51
|
+
'Org Unit 2'
|
|
52
|
+
)
|
|
53
|
+
.find('> [data-test="dhis2-uiwidgets-orgunittree-node-toggle"]')
|
|
54
|
+
.click()
|
|
55
|
+
.should('have.class', 'open')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
When('the user expands the root sub org unit', () => {
|
|
59
|
+
getRootOrgUnitByLabel('Org Unit 2')
|
|
60
|
+
.find('> [data-test="dhis2-uiwidgets-orgunittree-node-toggle"]')
|
|
61
|
+
.click()
|
|
62
|
+
.should('have.class', 'open')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
Then('the root unit is closed', () => {
|
|
66
|
+
cy.getOrgUnitByLabel('Org Unit 1').shouldBeAClosedNode()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
Then('the root unit is opened', () => {
|
|
70
|
+
cy.getOrgUnitByLabel('Org Unit 1').shouldBeAnOrgUnitNode()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
Then('the root sub org unit should not expand', () => {
|
|
74
|
+
getRootOrgUnitByLabel('Org Unit 2')
|
|
75
|
+
.find('> [data-test="dhis2-uiwidgets-orgunittree-node-toggle"]')
|
|
76
|
+
.should('not.have.class', 'open')
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
Then('the sub org unit within the main org unit tree should not expand', () => {
|
|
80
|
+
getRootOrgUnitByLabel('Org Unit 1')
|
|
81
|
+
.contains(
|
|
82
|
+
'[data-test="dhis2-uiwidgets-orgunittree-node"]',
|
|
83
|
+
'Org Unit 2'
|
|
84
|
+
)
|
|
85
|
+
.find('> [data-test="dhis2-uiwidgets-orgunittree-node-toggle"]')
|
|
86
|
+
.should('not.have.class', 'open')
|
|
87
|
+
})
|