@dhis2-ui/organisation-unit-tree 10.16.2 → 10.16.3-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/package.json +8 -7
  2. package/src/__e2e__/children_as_child_nodes.js +23 -0
  3. package/src/__e2e__/common.js +70 -0
  4. package/src/__e2e__/controlled_expanded.js +89 -0
  5. package/src/__e2e__/displaying_loading_error.js +45 -0
  6. package/src/__e2e__/expanded.js +42 -0
  7. package/src/__e2e__/force_reload.js +66 -0
  8. package/src/__e2e__/get-organisation-unit-data.js +119 -0
  9. package/src/__e2e__/highlight.js +23 -0
  10. package/src/__e2e__/loading_state.js +37 -0
  11. package/src/__e2e__/multi_selection.js +24 -0
  12. package/src/__e2e__/namespace.js +1 -0
  13. package/src/__e2e__/no_selection.js +32 -0
  14. package/src/__e2e__/path_based_filtering.js +49 -0
  15. package/src/__e2e__/single_selection.js +46 -0
  16. package/src/__e2e__/sub_unit_as_root.js +28 -0
  17. package/src/__e2e__/tree_api.js +55 -0
  18. package/src/__stories__/collapsed.js +11 -0
  19. package/src/__stories__/custom-expanded-imperative-open.js +181 -0
  20. package/src/__stories__/custom-node-label.js +19 -0
  21. package/src/__stories__/development-stories.js +86 -0
  22. package/src/__stories__/expanded.js +12 -0
  23. package/src/__stories__/filtered-root.js +15 -0
  24. package/src/__stories__/filtered.js +13 -0
  25. package/src/__stories__/force-reload-all.js +46 -0
  26. package/src/__stories__/force-reload-one-unit.js +36 -0
  27. package/src/__stories__/highlighted.js +13 -0
  28. package/src/__stories__/indeterminate.js +13 -0
  29. package/src/__stories__/loading-error-grandchild.js +39 -0
  30. package/src/__stories__/loading.js +27 -0
  31. package/src/__stories__/multiple-roots.js +20 -0
  32. package/src/__stories__/no-selection.js +16 -0
  33. package/src/__stories__/replace-roots.js +28 -0
  34. package/src/__stories__/root-error.js +36 -0
  35. package/src/__stories__/root-loading.js +34 -0
  36. package/src/__stories__/rtl.js +14 -0
  37. package/src/__stories__/selected-multiple.js +18 -0
  38. package/src/__stories__/shared.js +192 -0
  39. package/src/__stories__/single-selection.js +16 -0
  40. package/src/features/children_as_child_nodes/index.js +29 -0
  41. package/src/features/children_as_child_nodes.feature +6 -0
  42. package/src/features/controlled_expanded/index.js +86 -0
  43. package/src/features/controlled_expanded.feature +11 -0
  44. package/src/features/displaying_loading_error/index.js +46 -0
  45. package/src/features/displaying_loading_error.feature +24 -0
  46. package/src/features/expanded/index.js +87 -0
  47. package/src/features/expanded.feature +27 -0
  48. package/src/features/force_reload/index.js +36 -0
  49. package/src/features/force_reload.feature +7 -0
  50. package/src/features/highlight/index.js +9 -0
  51. package/src/features/highlight.feature +5 -0
  52. package/src/features/loading_state/index.js +26 -0
  53. package/src/features/loading_state.feature +7 -0
  54. package/src/features/multi_selection/index.js +94 -0
  55. package/src/features/multi_selection.feature +31 -0
  56. package/src/features/no_selection/index.js +41 -0
  57. package/src/features/no_selection.feature +13 -0
  58. package/src/features/path_based_filtering/index.js +97 -0
  59. package/src/features/path_based_filtering.feature +24 -0
  60. package/src/features/single_selection/index.js +41 -0
  61. package/src/features/single_selection.feature +20 -0
  62. package/src/features/sub_unit_as_root/index.js +83 -0
  63. package/src/features/sub_unit_as_root.feature +34 -0
  64. package/src/features/tree_api/index.js +121 -0
  65. package/src/features/tree_api.feature +37 -0
  66. package/src/get-all-expanded-paths/get-all-expanded-paths.js +32 -0
  67. package/src/get-all-expanded-paths/get-all-expanded-paths.test.js +22 -0
  68. package/src/get-all-expanded-paths/index.js +1 -0
  69. package/src/helpers/index.js +3 -0
  70. package/src/helpers/is-path-included.js +15 -0
  71. package/src/helpers/left-trim-to-root-id.js +3 -0
  72. package/src/helpers/sort-node-children-alphabetically.js +5 -0
  73. package/src/index.js +6 -0
  74. package/src/locales/ar/translations.json +5 -0
  75. package/src/locales/cs/translations.json +5 -0
  76. package/src/locales/en/translations.json +5 -0
  77. package/src/locales/es/translations.json +5 -0
  78. package/src/locales/es_419/translations.json +5 -0
  79. package/src/locales/fr/translations.json +5 -0
  80. package/src/locales/index.js +50 -0
  81. package/src/locales/lo/translations.json +5 -0
  82. package/src/locales/nb/translations.json +5 -0
  83. package/src/locales/nl/translations.json +5 -0
  84. package/src/locales/pt/translations.json +5 -0
  85. package/src/locales/ru/translations.json +5 -0
  86. package/src/locales/uk/translations.json +5 -0
  87. package/src/locales/uz_Latn/translations.json +5 -0
  88. package/src/locales/uz_UZ_Cyrl/translations.json +5 -0
  89. package/src/locales/uz_UZ_Latn/translations.json +5 -0
  90. package/src/locales/vi/translations.json +5 -0
  91. package/src/locales/zh/translations.json +5 -0
  92. package/src/locales/zh_CN/translations.json +5 -0
  93. package/src/organisation-unit-node/compute-child-nodes.js +27 -0
  94. package/src/organisation-unit-node/compute-child-nodes.test.js +85 -0
  95. package/src/organisation-unit-node/error-message.js +23 -0
  96. package/src/organisation-unit-node/has-descendant-selected-paths.js +15 -0
  97. package/src/organisation-unit-node/has-descendant-selected-paths.test.js +30 -0
  98. package/src/organisation-unit-node/index.js +1 -0
  99. package/src/organisation-unit-node/label/disabled-selection-label.js +26 -0
  100. package/src/organisation-unit-node/label/icon-empty.js +31 -0
  101. package/src/organisation-unit-node/label/icon-folder-closed.js +38 -0
  102. package/src/organisation-unit-node/label/icon-folder-open.js +49 -0
  103. package/src/organisation-unit-node/label/icon-single.js +41 -0
  104. package/src/organisation-unit-node/label/icon.js +35 -0
  105. package/src/organisation-unit-node/label/iconized-checkbox.js +67 -0
  106. package/src/organisation-unit-node/label/index.js +1 -0
  107. package/src/organisation-unit-node/label/label-container.js +36 -0
  108. package/src/organisation-unit-node/label/label.js +146 -0
  109. package/src/organisation-unit-node/label/single-selection-label.js +60 -0
  110. package/src/organisation-unit-node/loading-spinner.js +22 -0
  111. package/src/organisation-unit-node/organisation-unit-node-children.js +123 -0
  112. package/src/organisation-unit-node/organisation-unit-node.js +190 -0
  113. package/src/organisation-unit-node/use-open-state.js +37 -0
  114. package/src/organisation-unit-node/use-open-state.test.js +111 -0
  115. package/src/organisation-unit-node/use-org-children.js +63 -0
  116. package/src/organisation-unit-node/use-org-children.test.js +314 -0
  117. package/src/organisation-unit-node/use-org-data/index.js +1 -0
  118. package/src/organisation-unit-node/use-org-data/use-org-data.js +40 -0
  119. package/src/organisation-unit-node/use-org-data/use-org-data.test.js +137 -0
  120. package/src/organisation-unit-tree/default-render-node-label/default-render-node-label.js +1 -0
  121. package/src/organisation-unit-tree/default-render-node-label/index.js +1 -0
  122. package/src/organisation-unit-tree/filter-root-ids.js +9 -0
  123. package/src/organisation-unit-tree/index.js +3 -0
  124. package/src/organisation-unit-tree/organisation-unit-tree-root-error.js +20 -0
  125. package/src/organisation-unit-tree/organisation-unit-tree-root-loading.js +22 -0
  126. package/src/organisation-unit-tree/organisation-unit-tree.js +253 -0
  127. package/src/organisation-unit-tree/organisation-unit-tree.test.js +77 -0
  128. package/src/organisation-unit-tree/use-expanded/create-expand-handlers.js +45 -0
  129. package/src/organisation-unit-tree/use-expanded/create-expand-handlers.test.js +54 -0
  130. package/src/organisation-unit-tree/use-expanded/index.js +1 -0
  131. package/src/organisation-unit-tree/use-expanded/use-expanded.js +42 -0
  132. package/src/organisation-unit-tree/use-expanded/use-expanded.test.js +73 -0
  133. package/src/organisation-unit-tree/use-force-reload.js +22 -0
  134. package/src/organisation-unit-tree/use-force-reload.test.js +43 -0
  135. package/src/organisation-unit-tree/use-root-org-data/index.js +1 -0
  136. package/src/organisation-unit-tree/use-root-org-data/patch-missing-display-name.js +20 -0
  137. package/src/organisation-unit-tree/use-root-org-data/patch-missing-display-name.test.js +24 -0
  138. package/src/organisation-unit-tree/use-root-org-data/use-root-org-data.js +60 -0
  139. package/src/organisation-unit-tree/use-root-org-data/use-root-org-unit.test.js +184 -0
  140. package/src/organisation-unit-tree.e2e.stories.js +28 -0
  141. package/src/organisation-unit-tree.prod.stories.js +70 -0
  142. package/src/prop-types.js +33 -0
@@ -0,0 +1,121 @@
1
+ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ const expectStubPayloadToEqual = (stub, prop, expected) => {
4
+ const calls = stub.getCalls()
5
+ const { args } = calls[calls.length - 1]
6
+ const [payload] = args
7
+ expect(payload[prop]).to.eql(expected)
8
+ }
9
+
10
+ Given('an OrganisationUnitTree is rendered', () => {
11
+ cy.visitStory('OrganisationUnitTree', 'Events')
12
+ cy.getOrgUnitByLabel('Org Unit 1').shouldBeDoneLoading()
13
+ })
14
+
15
+ Given('a node has been selected', () => {
16
+ cy.getOrgUnitByLabel('Org Unit 1').toggleOrgUnitNodeSelection(true)
17
+ })
18
+
19
+ Given('a node with children is rendered', () => {
20
+ cy.visitStory('OrganisationUnitTree', 'Events')
21
+ })
22
+
23
+ Given('the node has been expanded', () => {
24
+ cy.getOrgUnitByLabel('Org Unit 1').openOrgUnitNode()
25
+ })
26
+
27
+ Given("the children haven't been loaded yet", () => {
28
+ cy.window().should((win) => {
29
+ expect(win.onExpand).not.to.be.called
30
+ })
31
+ })
32
+
33
+ When('a node gets selected', () => {
34
+ cy.getOrgUnitByLabel('Org Unit 1').toggleOrgUnitNodeSelection(true)
35
+ })
36
+
37
+ When('a node gets deselected', () => {
38
+ cy.getOrgUnitByLabel('Org Unit 1').toggleOrgUnitNodeSelection(false)
39
+ })
40
+
41
+ When('the node is expanded', () => {
42
+ cy.getOrgUnitByLabel('Org Unit 1').openOrgUnitNode()
43
+ })
44
+
45
+ When('a node is collapsed', () => {
46
+ cy.getOrgUnitByLabel('Org Unit 1').closeOrgUnitNode()
47
+ })
48
+
49
+ When('the children have been loaded', () => {
50
+ cy.getOrgUnitByLabel('Org Unit 1').toggleOrgUnitNode(true)
51
+ })
52
+
53
+ Then('the onChange callback gets called', () => {
54
+ cy.window().should((win) => {
55
+ expect(win.onChange).to.be.called
56
+ })
57
+ })
58
+
59
+ Then('the payload includes the path of the selected node', () => {
60
+ cy.window().should((win) => {
61
+ expectStubPayloadToEqual(win.onChange, 'path', '/A0000000000')
62
+ })
63
+ })
64
+
65
+ Then('the payload includes checked which is set to "true"', () => {
66
+ cy.window().should((win) => {
67
+ expectStubPayloadToEqual(win.onChange, 'checked', true)
68
+ })
69
+ })
70
+
71
+ Then('the payload includes all selected nodes', () => {
72
+ cy.window().should((win) => {
73
+ expectStubPayloadToEqual(win.onChange, 'selected', ['/A0000000000'])
74
+ })
75
+ })
76
+
77
+ Then('the payload includes checked which is set to "false"', () => {
78
+ cy.window().should((win) => {
79
+ expectStubPayloadToEqual(win.onChange, 'checked', false)
80
+ })
81
+ })
82
+
83
+ Then('the onExpand callback gets called', () => {
84
+ cy.window().should((win) => {
85
+ expect(win.onExpand).to.be.called
86
+ })
87
+ })
88
+
89
+ Then('the payload includes the path of the expanded node', () => {
90
+ cy.window().should((win) => {
91
+ expectStubPayloadToEqual(win.onExpand, 'path', '/A0000000000')
92
+ })
93
+ })
94
+
95
+ Then('the onCollapse callback gets called', () => {
96
+ cy.window().should((win) => {
97
+ expect(win.onCollapse).to.be.called
98
+ })
99
+ })
100
+
101
+ Then('the payload includes the path of the collapsed node', () => {
102
+ cy.window().should((win) => {
103
+ expectStubPayloadToEqual(win.onCollapse, 'path', '/A0000000000')
104
+ })
105
+ })
106
+
107
+ Then('the onChildrenLoaded callback gets called', () => {
108
+ cy.window().should((win) => {
109
+ expect(win.onChildrenLoaded).to.be.called
110
+ })
111
+ })
112
+
113
+ Then("the payload contains the loaded children's data", () => {
114
+ cy.window().should((win) => {
115
+ const calls = win.onChildrenLoaded.getCalls()
116
+ expect(calls).to.have.length(1)
117
+
118
+ const [{ args }] = calls
119
+ expect(args[0].id).to.equal('A0000000000')
120
+ })
121
+ })
@@ -0,0 +1,37 @@
1
+ Feature: The OrganisationUnitTree offers props for its events
2
+
3
+ Scenario: The user selects a node
4
+ Given an OrganisationUnitTree is rendered
5
+ When a node gets selected
6
+ Then the onChange callback gets called
7
+ And the payload includes the path of the selected node
8
+ And the payload includes checked which is set to "true"
9
+ And the payload includes all selected nodes
10
+
11
+ Scenario: The user deselects a node
12
+ Given an OrganisationUnitTree is rendered
13
+ And a node has been selected
14
+ When a node gets deselected
15
+ Then the onChange callback gets called
16
+ And the payload includes the path of the selected node
17
+ And the payload includes checked which is set to "false"
18
+
19
+ Scenario: The user expands a node with children
20
+ Given a node with children is rendered
21
+ When the node is expanded
22
+ Then the onExpand callback gets called
23
+ And the payload includes the path of the expanded node
24
+
25
+ Scenario: The user collapses a node with children
26
+ Given a node with children is rendered
27
+ And the node has been expanded
28
+ When a node is collapsed
29
+ Then the onCollapse callback gets called
30
+ And the payload includes the path of the collapsed node
31
+
32
+ Scenario: Children of a node are done being loaded
33
+ Given a node with children is rendered
34
+ But the children haven't been loaded yet
35
+ When the children have been loaded
36
+ Then the onChildrenLoaded callback gets called
37
+ And the payload contains the loaded children's data
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @template T
3
+ * @param {Array.<T>} arr
4
+ * @returns {Array.<T>}
5
+ */
6
+ const removeDuplicates = (arr) => Array.from(new Set(arr))
7
+
8
+ /**
9
+ * @param {string} path
10
+ * @returns {string[]}
11
+ */
12
+ const extractAllPathsFromPath = (path) => {
13
+ // remove leading slash and split by path delimiter/slashes
14
+ const segments = path.replace(/^\//, '').split('/')
15
+
16
+ const withSubPaths = segments.map((segment, index) => {
17
+ // take all segments from 0 to index and join them with the delimiter
18
+ return `/${segments.slice(0, index + 1).join('/')}`
19
+ })
20
+
21
+ return removeDuplicates(withSubPaths)
22
+ }
23
+
24
+ /**
25
+ * @param {string[]} initiallyExpanded
26
+ * @returns {string[]}
27
+ */
28
+ export const getAllExpandedPaths = (initiallyExpanded) =>
29
+ initiallyExpanded.reduce((all, curPath) => {
30
+ const allPathsInCurPath = extractAllPathsFromPath(curPath)
31
+ return [...all, ...allPathsInCurPath]
32
+ }, [])
@@ -0,0 +1,22 @@
1
+ import { getAllExpandedPaths } from './get-all-expanded-paths.js'
2
+
3
+ describe('OrganisationUnitTree - getAllExpandedPaths', () => {
4
+ const initiallyExpanded = ['/foo/bar/baz', '/foobar/barbaz/bazfoo']
5
+
6
+ it('should include all initiallyExpanded paths in the returned expanded array', () => {
7
+ const actual = getAllExpandedPaths(initiallyExpanded)
8
+ const expected = expect.arrayContaining(initiallyExpanded)
9
+ expect(actual).toEqual(expected)
10
+ })
11
+
12
+ it('should include all sub paths of the paths in initiallyExpanded in the returned exanded array', () => {
13
+ const actual = getAllExpandedPaths(initiallyExpanded)
14
+ const expected = expect.arrayContaining([
15
+ '/foo',
16
+ '/foo/bar',
17
+ '/foobar',
18
+ '/foobar/barbaz',
19
+ ])
20
+ expect(actual).toEqual(expected)
21
+ })
22
+ })
@@ -0,0 +1 @@
1
+ export { getAllExpandedPaths } from './get-all-expanded-paths.js'
@@ -0,0 +1,3 @@
1
+ export { isPathIncluded } from './is-path-included.js'
2
+ export { leftTrimToRootId } from './left-trim-to-root-id.js'
3
+ export { sortNodeChildrenAlphabetically } from './sort-node-children-alphabetically.js'
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @param {string[]} includedPaths
3
+ * @param {string} path
4
+ * @returns {bool}
5
+ */
6
+ export const isPathIncluded = (includedPaths, path) => {
7
+ const isIncluded = includedPaths.some((includedPath) => {
8
+ if (path === includedPath) {
9
+ return true
10
+ }
11
+ return includedPath.startsWith(`${path}/`)
12
+ })
13
+
14
+ return isIncluded
15
+ }
@@ -0,0 +1,3 @@
1
+ export const leftTrimToRootId = (path, rootId) => {
2
+ return path.replace(new RegExp(`^.*(/${rootId})`), '$1')
3
+ }
@@ -0,0 +1,5 @@
1
+ // sort mutates the original
2
+ export const sortNodeChildrenAlphabetically = (children) =>
3
+ [...children].sort((left, right) =>
4
+ left.displayName.localeCompare(right.displayName)
5
+ )
package/src/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export {
2
+ OrganisationUnitTree,
3
+ OrganisationUnitTreeRootError,
4
+ OrganisationUnitTreeRootLoading,
5
+ } from './organisation-unit-tree/index.js'
6
+ export { getAllExpandedPaths as getAllExpandedOrgUnitPaths } from './get-all-expanded-paths/index.js'
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "",
3
+ "Could not load children": "تعذر تحميل الأبناء",
4
+ "Error: {{ ERRORMESSAGE }}": "خطأ: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Filtru neodpovídají žádné podřízené",
3
+ "Could not load children": "Nelze načíst podřazené",
4
+ "Error: {{ ERRORMESSAGE }}": "Chyba: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "No children match filter",
3
+ "Could not load children": "Could not load children",
4
+ "Error: {{ ERRORMESSAGE }}": "Error: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Sin filtros de coincidencia en los elementos inferiores",
3
+ "Could not load children": "No se pudo cargar los elementos inferiores",
4
+ "Error: {{ ERRORMESSAGE }}": "Error: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Ningún descendiente coincide con el filtro",
3
+ "Could not load children": "No se han podido cargar los descendientes",
4
+ "Error: {{ ERRORMESSAGE }}": "Error: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Aucun enfant ne correspond au filtre",
3
+ "Could not load children": "Impossible de charger les enfants",
4
+ "Error: {{ ERRORMESSAGE }}": "Erreur : {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,50 @@
1
+ //------------------------------------------------------------------------------
2
+ // <auto-generated>
3
+ // This code was generated by d2-i18n-generate.
4
+ //
5
+ // Changes to this file may cause incorrect behavior and will be lost if
6
+ // the code is regenerated.
7
+ // </auto-generated>
8
+ //------------------------------------------------------------------------------
9
+ import i18n from '@dhis2/d2-i18n'
10
+
11
+ import arTranslations from './ar/translations.json'
12
+ import csTranslations from './cs/translations.json'
13
+ import enTranslations from './en/translations.json'
14
+ import esTranslations from './es/translations.json'
15
+ import es_419Translations from './es_419/translations.json'
16
+ import frTranslations from './fr/translations.json'
17
+ import loTranslations from './lo/translations.json'
18
+ import nbTranslations from './nb/translations.json'
19
+ import nlTranslations from './nl/translations.json'
20
+ import ptTranslations from './pt/translations.json'
21
+ import ruTranslations from './ru/translations.json'
22
+ import ukTranslations from './uk/translations.json'
23
+ import uz_LatnTranslations from './uz_Latn/translations.json'
24
+ import uz_UZ_CyrlTranslations from './uz_UZ_Cyrl/translations.json'
25
+ import uz_UZ_LatnTranslations from './uz_UZ_Latn/translations.json'
26
+ import viTranslations from './vi/translations.json'
27
+ import zhTranslations from './zh/translations.json'
28
+ import zh_CNTranslations from './zh_CN/translations.json'
29
+
30
+ const namespace = 'default'
31
+ i18n.addResources('ar', namespace, arTranslations)
32
+ i18n.addResources('cs', namespace, csTranslations)
33
+ i18n.addResources('en', namespace, enTranslations)
34
+ i18n.addResources('es', namespace, esTranslations)
35
+ i18n.addResources('es_419', namespace, es_419Translations)
36
+ i18n.addResources('fr', namespace, frTranslations)
37
+ i18n.addResources('lo', namespace, loTranslations)
38
+ i18n.addResources('nb', namespace, nbTranslations)
39
+ i18n.addResources('nl', namespace, nlTranslations)
40
+ i18n.addResources('pt', namespace, ptTranslations)
41
+ i18n.addResources('ru', namespace, ruTranslations)
42
+ i18n.addResources('uk', namespace, ukTranslations)
43
+ i18n.addResources('uz_Latn', namespace, uz_LatnTranslations)
44
+ i18n.addResources('uz_UZ_Cyrl', namespace, uz_UZ_CyrlTranslations)
45
+ i18n.addResources('uz_UZ_Latn', namespace, uz_UZ_LatnTranslations)
46
+ i18n.addResources('vi', namespace, viTranslations)
47
+ i18n.addResources('zh', namespace, zhTranslations)
48
+ i18n.addResources('zh_CN', namespace, zh_CNTranslations)
49
+
50
+ export default i18n
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "ບໍ່ພົບເດັກນ້ອຍທີ່ກົງກັບຕົວກອງ",
3
+ "Could not load children": "ບໍ່ສາມາດໂຫຼດເດັກນ້ອຍໄດ້",
4
+ "Error: {{ ERRORMESSAGE }}": "ຂໍ້ຜິດພາດ {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Ingen underenheter matcher filter",
3
+ "Could not load children": "Kunne ikke laste underordnede enheter",
4
+ "Error: {{ ERRORMESSAGE }}": "Feil: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Er komen geen kinderen overeen met het filter",
3
+ "Could not load children": "Kan kinderen niet laden",
4
+ "Error: {{ ERRORMESSAGE }}": "Fout: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Nenhum filho corresponde ao filtro",
3
+ "Could not load children": "Não foi possível carregar os filhos",
4
+ "Error: {{ ERRORMESSAGE }}": "Erro: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Нет дочерних единиц, соответствующих фильтру",
3
+ "Could not load children": "¨Не удалось загрузить дочерние единицы",
4
+ "Error: {{ ERRORMESSAGE }}": "Ошибка: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Немає дочірніх об'єктів, які відповідають фільтру",
3
+ "Could not load children": "Не вдалося завантажити дочірні об'єкти",
4
+ "Error: {{ ERRORMESSAGE }}": "Помилка: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "",
3
+ "Could not load children": "Bolalar yuklanmadi",
4
+ "Error: {{ ERRORMESSAGE }}": ""
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Фильтрга мос бўлган авлод мавжуд эмас",
3
+ "Could not load children": "Болалар юкланмади",
4
+ "Error: {{ ERRORMESSAGE }}": "Хатолик: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "",
3
+ "Could not load children": "Bolalar yuklanmadi",
4
+ "Error: {{ ERRORMESSAGE }}": ""
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "Không có đơn vị con phù hợp với bộ lọc",
3
+ "Could not load children": "Không thể tải đơn vị con",
4
+ "Error: {{ ERRORMESSAGE }}": "Lỗi: {{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "没有下属匹配过滤器",
3
+ "Could not load children": "无法加载下属",
4
+ "Error: {{ ERRORMESSAGE }}": "错误:{{ ERRORMESSAGE }}"
5
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "No children match filter": "",
3
+ "Could not load children": "无法加载下属",
4
+ "Error: {{ ERRORMESSAGE }}": ""
5
+ }
@@ -0,0 +1,27 @@
1
+ import { isPathIncluded } from '../helpers/index.js'
2
+
3
+ /**
4
+ * Returns all the childrenIds that should be rendered.
5
+ * An id will be included if it's parent's path + the id is inside
6
+ * the "filter" or the parent's path + id is a substring
7
+ * of the paths in "filter" (then it's a parent path of
8
+ * the units that should be included itself)
9
+ *
10
+ * @param {Object} node
11
+ * @param {Object[]} node.children
12
+ * @param {string[]} includedPaths
13
+ * @returns {string[]}
14
+ */
15
+ export const computeChildNodes = (node, filter) => {
16
+ if (!node.children) {
17
+ return []
18
+ }
19
+
20
+ if (!filter.length) {
21
+ return node.children
22
+ }
23
+
24
+ return node.children.filter((child) => {
25
+ return isPathIncluded(filter, `${node.path}/${child.id}`)
26
+ })
27
+ }
@@ -0,0 +1,85 @@
1
+ import { computeChildNodes } from './compute-child-nodes.js'
2
+
3
+ describe('computeChildNodes', () => {
4
+ let node = { path: 'foo/bar' }
5
+ const defaultChildren = [
6
+ { id: 'foobar' },
7
+ { id: 'barbaz' },
8
+ { id: 'foobarbaz' },
9
+ ]
10
+ const defaultChildrenIds = defaultChildren
11
+ const irrelevantPaths = ['irrelevant/path']
12
+ const pathsToIncludeWithMatchingAncestors = [
13
+ 'foo/bar/foobar/barbaz',
14
+ 'foo/bar/barbaz/bazbazfoo',
15
+ ]
16
+ const pathsToIncludeWithMatchingChildren = ['foo/bar/foobar']
17
+ const pathsToIncludeWithMatchingParents = ['foo/bar']
18
+
19
+ beforeEach(() => {
20
+ node = { path: 'foo/bar' }
21
+ })
22
+
23
+ it('should return all children when filter is empty', () => {
24
+ node.children = defaultChildren
25
+ const filter = []
26
+ const actual = computeChildNodes(node, filter)
27
+ const expected = defaultChildrenIds
28
+
29
+ expect(actual).toEqual(expected)
30
+ })
31
+
32
+ it("should return an empty array when the node has children but neither the children's or the parent path are in filter", () => {
33
+ node.children = defaultChildren
34
+ const filter = irrelevantPaths
35
+ const actual = computeChildNodes(node, filter)
36
+ const expected = []
37
+
38
+ expect(actual).toEqual(expected)
39
+ })
40
+
41
+ it('should return an empty array when filter contains none of the child paths but a parent path', () => {
42
+ node.children = defaultChildren
43
+ const filter = pathsToIncludeWithMatchingParents
44
+ const actual = computeChildNodes(node, filter)
45
+ const expected = []
46
+
47
+ expect(actual).toEqual(expected)
48
+ })
49
+
50
+ it('should return some the children ids when filter contains none of the child paths but some ancestor paths', () => {
51
+ node.children = defaultChildren
52
+ const filter = pathsToIncludeWithMatchingAncestors
53
+ const actual = computeChildNodes(node, filter)
54
+ const expected = [{ id: 'foobar' }, { id: 'barbaz' }]
55
+
56
+ expect(actual).toEqual(expected)
57
+ })
58
+
59
+ it('should return one of the children ids when filter contains one of the child paths but no parent path', () => {
60
+ node.children = defaultChildren
61
+ const filter = pathsToIncludeWithMatchingChildren
62
+ const actual = computeChildNodes(node, filter)
63
+ const expected = [{ id: 'foobar' }]
64
+
65
+ expect(actual).toEqual(expected)
66
+ })
67
+
68
+ it('should return an empty array when the node has no children but filter is not empty', () => {
69
+ node.children = []
70
+ const filter = pathsToIncludeWithMatchingParents
71
+ const actual = computeChildNodes(node, filter)
72
+ const expected = []
73
+
74
+ expect(actual).toEqual(expected)
75
+ })
76
+
77
+ it('should return an empty array when the node has no children and filter is empty', () => {
78
+ node.children = []
79
+ const filter = []
80
+ const actual = computeChildNodes(node, filter)
81
+ const expected = []
82
+
83
+ expect(actual).toEqual(expected)
84
+ })
85
+ })
@@ -0,0 +1,23 @@
1
+ import { colors, spacers, theme } from '@dhis2/ui-constants'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+
5
+ export const ErrorMessage = ({ children, dataTest }) => (
6
+ <span data-test={`${dataTest}-error`}>
7
+ {children}
8
+
9
+ <style jsx>{`
10
+ span {
11
+ font-size: 14px;
12
+ color: ${colors.grey800};
13
+ margin: ${spacers.dp4};
14
+ color: ${theme.error};
15
+ }
16
+ `}</style>
17
+ </span>
18
+ )
19
+
20
+ ErrorMessage.propTypes = {
21
+ children: PropTypes.any.isRequired,
22
+ dataTest: PropTypes.string.isRequired,
23
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Checks wether there are descendants of a path in the
3
+ * array of selected paths
4
+ *
5
+ * @param {string} path
6
+ * @param {string[]} selected
7
+ * @returns {bool}
8
+ */
9
+ export const hasDescendantSelectedPaths = (path, selected) => {
10
+ return selected.some((selectedPath) => {
11
+ const isNotPath = !selectedPath.match(new RegExp(`${path}$`))
12
+ const isSubPath = selectedPath.match(new RegExp(path))
13
+ return isNotPath && isSubPath
14
+ })
15
+ }
@@ -0,0 +1,30 @@
1
+ import { hasDescendantSelectedPaths } from './has-descendant-selected-paths.js'
2
+
3
+ describe('hasDescendantSelectedPaths', () => {
4
+ it('should return true when the path is a parent path of one of the selected paths', () => {
5
+ const path = '/foo/bar'
6
+ const selected = ['/foo/bar/baz', '/foobar/barbaz/']
7
+ const expected = true
8
+ const actual = hasDescendantSelectedPaths(path, selected)
9
+
10
+ expect(actual).toBe(expected)
11
+ })
12
+
13
+ it('should return false when the paths is not a parent path but an exact match with one of the selected paths', () => {
14
+ const path = '/foo/bar/baz'
15
+ const selected = ['/foo/bar/baz', '/foobar/barbaz/']
16
+ const expected = false
17
+ const actual = hasDescendantSelectedPaths(path, selected)
18
+
19
+ expect(actual).toBe(expected)
20
+ })
21
+
22
+ it('should return false when the paths is neither a parent path nor an exact match with one of the selected paths', () => {
23
+ const path = '/foobarbaz/boofarzab'
24
+ const selected = ['/foo/bar/baz', '/foobar/barbaz/']
25
+ const expected = false
26
+ const actual = hasDescendantSelectedPaths(path, selected)
27
+
28
+ expect(actual).toBe(expected)
29
+ })
30
+ })
@@ -0,0 +1 @@
1
+ export { OrganisationUnitNode } from './organisation-unit-node.js'
@@ -0,0 +1,26 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+ import { SingleSelectionLabel } from './single-selection-label.js'
4
+
5
+ /**
6
+ * @param {Object} props
7
+ * @param {string} props.label
8
+ * @param {Function} props.onToggleOpen
9
+ * @param {bool} [props.loading]
10
+ * @returns {React.Component}
11
+ */
12
+ export const DisabledSelectionLabel = ({ children, loading, onToggleOpen }) => (
13
+ <SingleSelectionLabel
14
+ checked={false}
15
+ loading={loading}
16
+ onChange={onToggleOpen}
17
+ >
18
+ {children}
19
+ </SingleSelectionLabel>
20
+ )
21
+
22
+ DisabledSelectionLabel.propTypes = {
23
+ children: PropTypes.any.isRequired,
24
+ onToggleOpen: PropTypes.func.isRequired,
25
+ loading: PropTypes.bool,
26
+ }