@dhis2-ui/select 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.
Files changed (274) hide show
  1. package/package.json +17 -16
  2. package/src/index.js +9 -0
  3. package/src/locales/ar/translations.json +7 -0
  4. package/src/locales/ar_IQ/translations.json +7 -0
  5. package/src/locales/ckb/translations.json +7 -0
  6. package/src/locales/cs/translations.json +7 -0
  7. package/src/locales/da/translations.json +7 -0
  8. package/src/locales/en/translations.json +11 -0
  9. package/src/locales/en_US/translations.json +9 -0
  10. package/src/locales/es/translations.json +7 -0
  11. package/src/locales/es_419/translations.json +7 -0
  12. package/src/locales/fr/translations.json +7 -0
  13. package/src/locales/hi_IN/translations.json +7 -0
  14. package/src/locales/id/translations.json +7 -0
  15. package/src/locales/index.js +80 -0
  16. package/src/locales/km/translations.json +7 -0
  17. package/src/locales/lo/translations.json +7 -0
  18. package/src/locales/my/translations.json +7 -0
  19. package/src/locales/nb/translations.json +7 -0
  20. package/src/locales/nl/translations.json +7 -0
  21. package/src/locales/prs/translations.json +7 -0
  22. package/src/locales/ps/translations.json +7 -0
  23. package/src/locales/pt/translations.json +7 -0
  24. package/src/locales/pt_BR/translations.json +7 -0
  25. package/src/locales/ru/translations.json +7 -0
  26. package/src/locales/sv/translations.json +7 -0
  27. package/src/locales/tet/translations.json +7 -0
  28. package/src/locales/tg/translations.json +7 -0
  29. package/src/locales/uk/translations.json +7 -0
  30. package/src/locales/ur/translations.json +7 -0
  31. package/src/locales/uz_Latn/translations.json +7 -0
  32. package/src/locales/uz_UZ_Cyrl/translations.json +7 -0
  33. package/src/locales/uz_UZ_Latn/translations.json +7 -0
  34. package/src/locales/vi/translations.json +7 -0
  35. package/src/locales/zh/translations.json +9 -0
  36. package/src/locales/zh_CN/translations.json +7 -0
  37. package/src/multi-select/__tests__/multi-select.test.js +23 -0
  38. package/src/multi-select/__tests__/selection-list.test.js +102 -0
  39. package/src/multi-select/features/accepts_blur_cb/index.js +14 -0
  40. package/src/multi-select/features/accepts_blur_cb.feature +8 -0
  41. package/src/multi-select/features/accepts_focus_cb/index.js +14 -0
  42. package/src/multi-select/features/accepts_focus_cb.feature +6 -0
  43. package/src/multi-select/features/accepts_initial_focus/index.js +5 -0
  44. package/src/multi-select/features/accepts_initial_focus.feature +5 -0
  45. package/src/multi-select/features/accepts_loading/index.js +22 -0
  46. package/src/multi-select/features/accepts_loading.feature +14 -0
  47. package/src/multi-select/features/accepts_max_height/index.js +31 -0
  48. package/src/multi-select/features/accepts_max_height.feature +11 -0
  49. package/src/multi-select/features/accepts_placeholder/index.js +25 -0
  50. package/src/multi-select/features/accepts_placeholder.feature +10 -0
  51. package/src/multi-select/features/accepts_prefix/index.js +19 -0
  52. package/src/multi-select/features/accepts_prefix.feature +10 -0
  53. package/src/multi-select/features/allows_invalid_options/index.js +27 -0
  54. package/src/multi-select/features/allows_invalid_options.feature +12 -0
  55. package/src/multi-select/features/allows_selecting/index.js +81 -0
  56. package/src/multi-select/features/allows_selecting.feature +44 -0
  57. package/src/multi-select/features/can_be_cleared/index.js +22 -0
  58. package/src/multi-select/features/can_be_cleared.feature +6 -0
  59. package/src/multi-select/features/can_be_disabled/index.js +36 -0
  60. package/src/multi-select/features/can_be_disabled.feature +32 -0
  61. package/src/multi-select/features/can_be_empty/index.js +28 -0
  62. package/src/multi-select/features/can_be_empty.feature +16 -0
  63. package/src/multi-select/features/can_be_filtered/index.js +23 -0
  64. package/src/multi-select/features/can_be_filtered.feature +14 -0
  65. package/src/multi-select/features/can_be_opened_and_closed/index.js +32 -0
  66. package/src/multi-select/features/can_be_opened_and_closed.feature +41 -0
  67. package/src/multi-select/features/common/index.js +49 -0
  68. package/src/multi-select/features/duplicate_option_values/index.js +21 -0
  69. package/src/multi-select/features/duplicate_option_values.feature +7 -0
  70. package/src/multi-select/features/menu_width_matches_input/index.js +53 -0
  71. package/src/multi-select/features/menu_width_matches_input.feature +11 -0
  72. package/src/multi-select/features/position/index.js +160 -0
  73. package/src/multi-select/features/position.feature +46 -0
  74. package/src/multi-select/features/shows_selections/index.js +18 -0
  75. package/src/multi-select/features/shows_selections.feature +9 -0
  76. package/src/multi-select/filterable-menu.js +43 -0
  77. package/src/multi-select/index.js +1 -0
  78. package/src/multi-select/input.js +129 -0
  79. package/src/multi-select/menu.js +91 -0
  80. package/src/multi-select/multi-select.e2e.stories.js +407 -0
  81. package/src/multi-select/multi-select.js +148 -0
  82. package/src/multi-select/multi-select.prod.stories.js +408 -0
  83. package/src/multi-select/selection-list.js +79 -0
  84. package/src/multi-select-field/__tests__/multi-select-field.test.js +29 -0
  85. package/src/multi-select-field/features/accepts_help_text/index.js +9 -0
  86. package/src/multi-select-field/features/accepts_help_text.feature +5 -0
  87. package/src/multi-select-field/features/accepts_label/index.js +9 -0
  88. package/src/multi-select-field/features/accepts_label.feature +5 -0
  89. package/src/multi-select-field/features/accepts_validation_text/index.js +9 -0
  90. package/src/multi-select-field/features/accepts_validation_text.feature +5 -0
  91. package/src/multi-select-field/features/can_be_required/index.js +11 -0
  92. package/src/multi-select-field/features/can_be_required.feature +5 -0
  93. package/src/multi-select-field/features/has_default_clear_text/index.js +16 -0
  94. package/src/multi-select-field/features/has_default_clear_text.feature +6 -0
  95. package/src/multi-select-field/features/has_default_empty_text/index.js +13 -0
  96. package/src/multi-select-field/features/has_default_empty_text.feature +6 -0
  97. package/src/multi-select-field/features/has_default_filter_nomatch_text/index.js +17 -0
  98. package/src/multi-select-field/features/has_default_filter_nomatch_text.feature +7 -0
  99. package/src/multi-select-field/features/has_default_filter_placeholder/index.js +15 -0
  100. package/src/multi-select-field/features/has_default_filter_placeholder.feature +6 -0
  101. package/src/multi-select-field/features/has_default_loading_text/index.js +13 -0
  102. package/src/multi-select-field/features/has_default_loading_text.feature +6 -0
  103. package/src/multi-select-field/index.js +1 -0
  104. package/src/multi-select-field/multi-select-field.e2e.stories.js +45 -0
  105. package/src/multi-select-field/multi-select-field.js +178 -0
  106. package/src/multi-select-field/multi-select-field.prod.stories.js +137 -0
  107. package/src/multi-select-option/index.js +1 -0
  108. package/src/multi-select-option/multi-select-option.js +70 -0
  109. package/src/select/__tests__/select.test.js +31 -0
  110. package/src/select/debounce/debounce.js +30 -0
  111. package/src/select/debounce/debounce.spec.js +35 -0
  112. package/src/select/empty.js +28 -0
  113. package/src/select/filter-input.js +42 -0
  114. package/src/select/filterable-menu.js +106 -0
  115. package/src/select/index.js +14 -0
  116. package/src/select/input-clear-button.js +66 -0
  117. package/src/select/input-placeholder.js +30 -0
  118. package/src/select/input-prefix.js +32 -0
  119. package/src/select/input-wrapper.js +114 -0
  120. package/src/select/loading.js +33 -0
  121. package/src/select/menu-wrapper.js +52 -0
  122. package/src/select/no-match.js +28 -0
  123. package/src/select/option-helpers.js +34 -0
  124. package/src/select/select.js +256 -0
  125. package/src/simple-single-select/__stories__/DefaultPosition.js +37 -0
  126. package/src/simple-single-select/__stories__/Dense.js +17 -0
  127. package/src/simple-single-select/__stories__/Empty.js +15 -0
  128. package/src/simple-single-select/__stories__/EmptyWithEmptyNode.js +27 -0
  129. package/src/simple-single-select/__stories__/EmptyWithEmptyText.js +16 -0
  130. package/src/simple-single-select/__stories__/FlippedPosition.js +36 -0
  131. package/src/simple-single-select/__stories__/InfiniteLoading.js +65 -0
  132. package/src/simple-single-select/__stories__/Loading.js +15 -0
  133. package/src/simple-single-select/__stories__/ShiftedIntoView.js +35 -0
  134. package/src/simple-single-select/__stories__/WithClearButton.js +26 -0
  135. package/src/simple-single-select/__stories__/WithCustomLowMaxHeight.js +17 -0
  136. package/src/simple-single-select/__stories__/WithCustomOptions.js +78 -0
  137. package/src/simple-single-select/__stories__/WithDisabledOption.js +25 -0
  138. package/src/simple-single-select/__stories__/WithFilterField.js +30 -0
  139. package/src/simple-single-select/__stories__/WithInitialFocus.js +29 -0
  140. package/src/simple-single-select/__stories__/WithManyOptions.js +19 -0
  141. package/src/simple-single-select/__stories__/WithMultipleSelectsAndScrollableParent.js +65 -0
  142. package/src/simple-single-select/__stories__/WithNoMatchText.js +25 -0
  143. package/src/simple-single-select/__stories__/WithOnBlur.js +22 -0
  144. package/src/simple-single-select/__stories__/WithOnFocus.js +22 -0
  145. package/src/simple-single-select/__stories__/WithOptionsAndDisabled.js +17 -0
  146. package/src/simple-single-select/__stories__/WithOptionsAndLoading.js +23 -0
  147. package/src/simple-single-select/__stories__/WithOptionsAndLoadingText.js +24 -0
  148. package/src/simple-single-select/__stories__/WithPlaceholder.js +18 -0
  149. package/src/simple-single-select/__stories__/WithPlaceholderAndSelection.js +20 -0
  150. package/src/simple-single-select/__stories__/WithPrefix.js +17 -0
  151. package/src/simple-single-select/__stories__/WithPrefixAndSelection.js +20 -0
  152. package/src/simple-single-select/__stories__/WithRTL.js +29 -0
  153. package/src/simple-single-select/__stories__/WithScrollingParent.js +51 -0
  154. package/src/simple-single-select/__stories__/WithSelection.js +19 -0
  155. package/src/simple-single-select/__stories__/WithSelectionAndDisabled.js +20 -0
  156. package/src/simple-single-select/__stories__/WithServerSideFilteringDemo.js +245 -0
  157. package/src/simple-single-select/__stories__/WithSimpleFilteringDemo.js +50 -0
  158. package/src/simple-single-select/__stories__/WithoutOptionsAndLoading.js +16 -0
  159. package/src/simple-single-select/__stories__/WithoutSelection.js +17 -0
  160. package/src/simple-single-select/__stories__/options.js +96 -0
  161. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/CustomDataProvider.js +70 -0
  162. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/dataElements.js +4271 -0
  163. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/index.js +1 -0
  164. package/src/simple-single-select/features/keyboard-interactions.test.e2e.js +581 -0
  165. package/src/simple-single-select/features/menu-positioning.test.e2e.js +187 -0
  166. package/src/simple-single-select/index.js +2 -0
  167. package/src/simple-single-select/is-option-hidden.js +21 -0
  168. package/src/simple-single-select/menu/empty.js +27 -0
  169. package/src/simple-single-select/menu/filter.js +61 -0
  170. package/src/simple-single-select/menu/index.js +1 -0
  171. package/src/simple-single-select/menu/loading.js +38 -0
  172. package/src/simple-single-select/menu/menu.js +171 -0
  173. package/src/simple-single-select/menu/no-match.js +28 -0
  174. package/src/simple-single-select/menu/option.js +161 -0
  175. package/src/simple-single-select/menu/options-list.js +107 -0
  176. package/src/simple-single-select/selected-value/clear-button.js +71 -0
  177. package/src/simple-single-select/selected-value/container.js +146 -0
  178. package/src/simple-single-select/selected-value/index.js +1 -0
  179. package/src/simple-single-select/selected-value/placeholder.js +24 -0
  180. package/src/simple-single-select/selected-value/prefix.js +26 -0
  181. package/src/simple-single-select/selected-value/selected-value.js +174 -0
  182. package/src/simple-single-select/shared-prop-types.js +8 -0
  183. package/src/simple-single-select/simple-single-select.e2e.stories.js +216 -0
  184. package/src/simple-single-select/simple-single-select.js +389 -0
  185. package/src/simple-single-select/simple-single-select.prod.stories.js +41 -0
  186. package/src/simple-single-select/simple-single-select.test.js +1136 -0
  187. package/src/simple-single-select/use-handle-key-press/index.js +2 -0
  188. package/src/simple-single-select/use-handle-key-press/use-focus-option.js +24 -0
  189. package/src/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +259 -0
  190. package/src/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +147 -0
  191. package/src/simple-single-select/use-handle-key-press/use-handle-typing.js +79 -0
  192. package/src/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +62 -0
  193. package/src/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +38 -0
  194. package/src/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +60 -0
  195. package/src/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +52 -0
  196. package/src/simple-single-select/use-handle-key-press/use-page-down.js +50 -0
  197. package/src/simple-single-select/use-handle-key-press/use-page-up.js +50 -0
  198. package/src/simple-single-select/use-handle-key-press/utils.js +23 -0
  199. package/src/simple-single-select-field/index.js +1 -0
  200. package/src/simple-single-select-field/simple-single-select-field.js +198 -0
  201. package/src/simple-single-select-field/simple-single-select-field.prod.stories.js +188 -0
  202. package/src/simple-single-select-field/simple-single-select-field.test.js +144 -0
  203. package/src/single-select/__tests__/selection.test.js +102 -0
  204. package/src/single-select/__tests__/single-select.test.js +23 -0
  205. package/src/single-select/features/accepts_blur_cb/index.js +14 -0
  206. package/src/single-select/features/accepts_blur_cb.feature +8 -0
  207. package/src/single-select/features/accepts_focus_cb/index.js +14 -0
  208. package/src/single-select/features/accepts_focus_cb.feature +6 -0
  209. package/src/single-select/features/accepts_initial_focus/index.js +5 -0
  210. package/src/single-select/features/accepts_initial_focus.feature +5 -0
  211. package/src/single-select/features/accepts_loading/index.js +22 -0
  212. package/src/single-select/features/accepts_loading.feature +14 -0
  213. package/src/single-select/features/accepts_max_height/index.js +31 -0
  214. package/src/single-select/features/accepts_max_height.feature +11 -0
  215. package/src/single-select/features/accepts_placeholder/index.js +25 -0
  216. package/src/single-select/features/accepts_placeholder.feature +10 -0
  217. package/src/single-select/features/accepts_prefix/index.js +19 -0
  218. package/src/single-select/features/accepts_prefix.feature +10 -0
  219. package/src/single-select/features/allows_invalid_options/index.js +28 -0
  220. package/src/single-select/features/allows_invalid_options.feature +12 -0
  221. package/src/single-select/features/allows_selecting/index.js +38 -0
  222. package/src/single-select/features/allows_selecting.feature +21 -0
  223. package/src/single-select/features/can_be_cleared/index.js +22 -0
  224. package/src/single-select/features/can_be_cleared.feature +6 -0
  225. package/src/single-select/features/can_be_disabled/index.js +39 -0
  226. package/src/single-select/features/can_be_disabled.feature +32 -0
  227. package/src/single-select/features/can_be_empty/index.js +28 -0
  228. package/src/single-select/features/can_be_empty.feature +16 -0
  229. package/src/single-select/features/can_be_filtered/index.js +23 -0
  230. package/src/single-select/features/can_be_filtered.feature +14 -0
  231. package/src/single-select/features/can_be_opened_and_closed/index.js +32 -0
  232. package/src/single-select/features/can_be_opened_and_closed.feature +41 -0
  233. package/src/single-select/features/common/index.js +44 -0
  234. package/src/single-select/features/duplicate_option_values/index.js +26 -0
  235. package/src/single-select/features/duplicate_option_values.feature +7 -0
  236. package/src/single-select/features/menu_width_matches_input/index.js +53 -0
  237. package/src/single-select/features/menu_width_matches_input.feature +11 -0
  238. package/src/single-select/features/position/index.js +111 -0
  239. package/src/single-select/features/position.feature +26 -0
  240. package/src/single-select/features/shows_selections/index.js +9 -0
  241. package/src/single-select/features/shows_selections.feature +5 -0
  242. package/src/single-select/filterable-menu.js +41 -0
  243. package/src/single-select/index.js +1 -0
  244. package/src/single-select/input.js +101 -0
  245. package/src/single-select/menu.js +75 -0
  246. package/src/single-select/selection.js +60 -0
  247. package/src/single-select/single-select.e2e.stories.js +376 -0
  248. package/src/single-select/single-select.js +146 -0
  249. package/src/single-select/single-select.prod.stories.js +358 -0
  250. package/src/single-select-field/__tests__/single-select-field.test.js +29 -0
  251. package/src/single-select-field/features/accepts_help_text/index.js +9 -0
  252. package/src/single-select-field/features/accepts_help_text.feature +5 -0
  253. package/src/single-select-field/features/accepts_label/index.js +9 -0
  254. package/src/single-select-field/features/accepts_label.feature +5 -0
  255. package/src/single-select-field/features/accepts_validation_text/index.js +9 -0
  256. package/src/single-select-field/features/accepts_validation_text.feature +5 -0
  257. package/src/single-select-field/features/can_be_required/index.js +11 -0
  258. package/src/single-select-field/features/can_be_required.feature +5 -0
  259. package/src/single-select-field/features/has_default_clear_text/index.js +16 -0
  260. package/src/single-select-field/features/has_default_clear_text.feature +6 -0
  261. package/src/single-select-field/features/has_default_empty_text/index.js +13 -0
  262. package/src/single-select-field/features/has_default_empty_text.feature +6 -0
  263. package/src/single-select-field/features/has_default_filter_nomatch_text/index.js +17 -0
  264. package/src/single-select-field/features/has_default_filter_nomatch_text.feature +7 -0
  265. package/src/single-select-field/features/has_default_filter_placeholder/index.js +15 -0
  266. package/src/single-select-field/features/has_default_filter_placeholder.feature +6 -0
  267. package/src/single-select-field/features/has_default_loading_text/index.js +13 -0
  268. package/src/single-select-field/features/has_default_loading_text.feature +6 -0
  269. package/src/single-select-field/index.js +1 -0
  270. package/src/single-select-field/single-select-field.e2e.stories.js +45 -0
  271. package/src/single-select-field/single-select-field.js +178 -0
  272. package/src/single-select-field/single-select-field.prod.stories.js +146 -0
  273. package/src/single-select-option/index.js +1 -0
  274. package/src/single-select-option/single-select-option.js +84 -0
@@ -0,0 +1,245 @@
1
+ import { useDataEngine, useDataQuery } from '@dhis2/app-runtime'
2
+ import React, { useCallback, useEffect, useRef, useState } from 'react'
3
+ import { SimpleSingleSelect } from '../simple-single-select.js'
4
+ import { CustomDataProvider } from './simple-single-select-server-side-filtering/index.js'
5
+
6
+ function dataElementToOption({ id, displayName }) {
7
+ return { value: id, label: displayName }
8
+ }
9
+
10
+ function useLoadDataElementQuery(options) {
11
+ return useDataQuery(
12
+ {
13
+ result: {
14
+ resource: 'dataElements',
15
+ id: ({ id }) => id,
16
+ },
17
+ },
18
+ { ...options, lazy: true }
19
+ )
20
+ }
21
+
22
+ function useLoadDataElementsQuery(options) {
23
+ return useDataQuery(
24
+ {
25
+ result: {
26
+ resource: 'dataElements',
27
+ params: ({ page }) => ({ page, pageSize: 10 }),
28
+ },
29
+ },
30
+ options
31
+ )
32
+ }
33
+
34
+ function useLoadFilteredDataElementsQuery(customOptions) {
35
+ const engine = useDataEngine()
36
+ const timeout = useRef(null)
37
+ const abortController = useRef()
38
+ const [loading, setLoading] = useState(false)
39
+ const [error, setError] = useState(null)
40
+
41
+ const refetch = useCallback(
42
+ ({ searchTerm, page }) => {
43
+ if (timeout.current) {
44
+ clearTimeout(timeout.current)
45
+ abortController.current?.abort()
46
+ }
47
+
48
+ return new Promise((resolve, reject) => {
49
+ timeout.current = setTimeout(async () => {
50
+ try {
51
+ abortController.current = new AbortController()
52
+ setLoading(true)
53
+
54
+ const query = {
55
+ result: {
56
+ resource: 'dataElements',
57
+ params: {
58
+ filter: [`displayName:ilike:${searchTerm}`],
59
+ pageSize: 10,
60
+ page,
61
+ },
62
+ },
63
+ }
64
+
65
+ const options = {
66
+ ...customOptions,
67
+ signal: abortController.current.signal,
68
+ }
69
+
70
+ try {
71
+ const result = await engine.query(query, options)
72
+ resolve(result)
73
+ } catch (e) {
74
+ setError(e)
75
+ }
76
+
77
+ abortController.current = null
78
+ timeout.current = null
79
+ setLoading(false)
80
+ } catch (e) {
81
+ reject(e)
82
+ }
83
+ }, 200)
84
+ })
85
+ },
86
+ [abortController, engine, customOptions]
87
+ )
88
+
89
+ return { loading, error, refetch }
90
+ }
91
+
92
+ function ServerSideFilteringSelect() {
93
+ const [initializedSelectedLabel, setInitializedSelectedLabel] =
94
+ useState(false)
95
+ const [selectedOption, setSelectedOption] = useState({
96
+ value: 'fbfJHSPpUQD',
97
+ label: 'Loading...',
98
+ })
99
+
100
+ const [loadedOptions, setLoadedOptions] = useState([])
101
+ const [defaultPager, setDefaultPager] = useState({
102
+ page: 0,
103
+ pageSize: -1,
104
+ total: -1,
105
+ pageCount: -1,
106
+ })
107
+
108
+ const [searchTerm, _setSearchTerm] = useState('')
109
+ const [filteredOptions, setFilteredOptions] = useState([])
110
+ const [filterPager, setFilterPager] = useState({
111
+ page: 0,
112
+ pageSize: -1,
113
+ total: -1,
114
+ pageCount: -1,
115
+ })
116
+
117
+ const loadDataElementQuery = useLoadDataElementQuery({
118
+ onComplete: (data) => {
119
+ setSelectedOption(dataElementToOption(data.result.dataElements))
120
+ setInitializedSelectedLabel(true) // Done AFTER setting the option's label!
121
+ },
122
+ onError: () => {
123
+ setSelectedOption((option) => ({ ...option, label: option.value }))
124
+ setInitializedSelectedLabel(true) // Done AFTER setting the option's label!
125
+ },
126
+ })
127
+
128
+ const loadDataElementsQuery = useLoadDataElementsQuery({
129
+ onComplete: ({ result }) => {
130
+ setDefaultPager(result.pager)
131
+ setLoadedOptions((prevLoadedOptions) => [
132
+ ...prevLoadedOptions,
133
+ ...result.dataElements.map(dataElementToOption),
134
+ ])
135
+ },
136
+ onError: () => {
137
+ setDefaultPager((prevPager) => ({
138
+ ...prevPager,
139
+ pageCount: prevPager.page,
140
+ total: loadedOptions.length,
141
+ }))
142
+ },
143
+ })
144
+
145
+ const loadFilteredDataElementsQuery = useLoadFilteredDataElementsQuery({
146
+ onComplete: ({ result }) => {
147
+ setFilterPager(result.pager)
148
+ setFilteredOptions((prevFilteredOptions) => {
149
+ const newOptions = result.dataElements.map(dataElementToOption)
150
+
151
+ return result.pager.page === 1
152
+ ? newOptions
153
+ : [...prevFilteredOptions, ...newOptions]
154
+ })
155
+ },
156
+ onError: () => {
157
+ setFilterPager((prevPager) => ({
158
+ ...prevPager,
159
+ pageCount: prevPager.page,
160
+ total: filteredOptions.length,
161
+ }))
162
+ },
163
+ })
164
+
165
+ const loadingOptions =
166
+ loadDataElementsQuery.loading || loadFilteredDataElementsQuery.loading
167
+ const options = searchTerm ? filteredOptions : loadedOptions
168
+
169
+ const setSearchTerm = useCallback(
170
+ (nextSearchTerm) => {
171
+ _setSearchTerm(nextSearchTerm)
172
+
173
+ if (nextSearchTerm.trim()) {
174
+ loadFilteredDataElementsQuery.refetch({
175
+ page: 1,
176
+ searchTerm: nextSearchTerm,
177
+ })
178
+ }
179
+ },
180
+ [loadFilteredDataElementsQuery.refetch]
181
+ )
182
+
183
+ const loadNextPage = useCallback(() => {
184
+ const pager = searchTerm ? filterPager : defaultPager
185
+
186
+ if (pager.page === pager.pageCount || loadingOptions) {
187
+ return
188
+ }
189
+
190
+ if (searchTerm) {
191
+ loadFilteredDataElementsQuery.refetch({
192
+ page: pager.page + 1,
193
+ searchTerm,
194
+ })
195
+ } else {
196
+ loadDataElementsQuery.refetch({ page: pager.page + 1 })
197
+ }
198
+ }, [
199
+ filterPager,
200
+ defaultPager,
201
+ searchTerm,
202
+ loadingOptions,
203
+ loadFilteredDataElementsQuery.refetch,
204
+ loadDataElementsQuery.refetch,
205
+ ])
206
+
207
+ useEffect(
208
+ () => {
209
+ if (selectedOption?.value) {
210
+ loadDataElementQuery.refetch({ id: selectedOption.value })
211
+ } else {
212
+ setInitializedSelectedLabel(true)
213
+ }
214
+ },
215
+ // Needs to run on initial render only!
216
+ // eslint-disable-next-line react-hooks/exhaustive-deps
217
+ []
218
+ )
219
+
220
+ return (
221
+ <SimpleSingleSelect
222
+ name="demo"
223
+ disabled={!initializedSelectedLabel}
224
+ loading={loadingOptions}
225
+ options={options}
226
+ selected={selectedOption}
227
+ filterable
228
+ filterValue={searchTerm}
229
+ filterPlaceholder="search for 'ART' or 'ANC'"
230
+ onFilterChange={setSearchTerm}
231
+ noMatchText="No options were found"
232
+ empty="Please use the search input to find data elements"
233
+ onChange={setSelectedOption}
234
+ onEndReached={loadNextPage}
235
+ />
236
+ )
237
+ }
238
+
239
+ const Wrapper = () => (
240
+ <CustomDataProvider>
241
+ <ServerSideFilteringSelect />
242
+ </CustomDataProvider>
243
+ )
244
+
245
+ export { Wrapper as WithServerSideFilteringDemo }
@@ -0,0 +1,50 @@
1
+ import React, { useMemo, useState } from 'react'
2
+ import { SimpleSingleSelect } from '../simple-single-select.js'
3
+
4
+ const options = [
5
+ { label: 'No selection', value: '' },
6
+ { label: 'ANC 1st visit', value: 'anc_1st_visit' },
7
+ { label: 'ANC 2nd visit', value: 'anc_2nd_visit' },
8
+ { label: 'ARI treated follow-up', value: 'ari_treated_follow-up' },
9
+ { label: 'ARI treated with new', value: 'ari_treated_with_new' },
10
+ ]
11
+
12
+ function filterOptions(options, searchTerm) {
13
+ // If there is no search term, we want to return all options
14
+ if (!searchTerm) {
15
+ return options
16
+ }
17
+
18
+ // We're transform both the label and the search term to
19
+ // lower-case strings so the search is case-insensitive
20
+ return options.filter(({ label }) => {
21
+ return label.toLowerCase().startsWith(searchTerm.toLowerCase())
22
+ })
23
+ }
24
+
25
+ export const WithSimpleFilteringDemo = () => {
26
+ // Handle actual value changes
27
+ const [selected, setSelected] = useState({
28
+ value: '',
29
+ label: 'No selection',
30
+ })
31
+
32
+ // Handle filtering
33
+ const [searchTerm, setSearchTerm] = useState('')
34
+ const filteredOptions = useMemo(
35
+ () => filterOptions(options, searchTerm),
36
+ [searchTerm]
37
+ )
38
+
39
+ return (
40
+ <SimpleSingleSelect
41
+ filterable
42
+ filterValue={searchTerm}
43
+ idPrefix="simple"
44
+ options={filteredOptions}
45
+ selected={selected}
46
+ onChange={setSelected}
47
+ onFilterChange={setSearchTerm}
48
+ />
49
+ )
50
+ }
@@ -0,0 +1,16 @@
1
+ import React, { useState } from 'react'
2
+ import { SimpleSingleSelect } from '../simple-single-select.js'
3
+
4
+ export const WithoutOptionsAndLoading = () => {
5
+ const [selected, setSelected] = useState(null)
6
+
7
+ return (
8
+ <SimpleSingleSelect
9
+ loading
10
+ name="simple"
11
+ selected={selected}
12
+ onChange={setSelected}
13
+ options={[]}
14
+ />
15
+ )
16
+ }
@@ -0,0 +1,17 @@
1
+ import React, { useState } from 'react'
2
+ import { SimpleSingleSelect } from '../simple-single-select.js'
3
+ import { fiveOptions } from './options.js'
4
+
5
+ export const WithoutSelection = () => {
6
+ const [selected, setSelected] = useState(null)
7
+
8
+ return (
9
+ <SimpleSingleSelect
10
+ inputMaxHeight="50px"
11
+ name="simple"
12
+ selected={selected}
13
+ onChange={setSelected}
14
+ options={fiveOptions}
15
+ />
16
+ )
17
+ }
@@ -0,0 +1,96 @@
1
+ export const options = [
2
+ {
3
+ label: 'None',
4
+ value: '',
5
+ },
6
+ {
7
+ label: 'ANC 1st visit',
8
+ value: 'anc_1st_visit',
9
+ },
10
+ {
11
+ label: 'ANC 2nd visit',
12
+ value: 'anc_2nd_visit',
13
+ },
14
+ {
15
+ label: 'ANC 3rd visit',
16
+ value: 'anc_3rd_visit',
17
+ },
18
+ {
19
+ label: 'ANC 4th or more visits',
20
+ value: 'anc_4th_or_more_visits',
21
+ },
22
+ {
23
+ label: 'ARI treated with antibiotics (pneumonia) follow-up',
24
+ value: 'ari_treated_with_antibiotics_(pneumonia)_follow-up',
25
+ },
26
+ {
27
+ label: 'ARI treated with antibiotics (pneumonia) new',
28
+ value: 'ari_treated_with_antibiotics_(pneumonia)_new',
29
+ },
30
+ {
31
+ label: 'ARI treated with antibiotics (pneumonia) referrals',
32
+ value: 'ari_treated_with_antibiotics_(pneumonia)_referrals',
33
+ },
34
+ {
35
+ label: 'ARI treated without antibiotics (cough) follow-up',
36
+ value: 'ari_treated_without_antibiotics_(cough)_follow-up',
37
+ },
38
+ {
39
+ label: 'ARI treated without antibiotics (cough) new',
40
+ value: 'ari_treated_without_antibiotics_(cough)_new',
41
+ },
42
+ {
43
+ label: 'ARI treated without antibiotics (cough) referrals',
44
+ value: 'ari_treated_without_antibiotics_(cough)_referrals',
45
+ },
46
+ {
47
+ label: 'ART No clients who stopped TRT due to TRT failure',
48
+ value: 'art_no_clients_who_stopped_trt_due_to_trt_failure',
49
+ },
50
+ {
51
+ label: 'ART No clients who stopped TRT due to adverse clinical status/event',
52
+ value: 'art_no_clients_who_stopped_trt_due_to_adverse_clinical_status/event',
53
+ },
54
+ {
55
+ label: 'ART No clients with change of regimen due to drug toxicity',
56
+ value: 'art_no_clients_with_change_of_regimen_due_to_drug_toxicity',
57
+ },
58
+ {
59
+ label: 'ART No clients with new adverse drug reaction',
60
+ value: 'art_no_clients_with_new_adverse_drug_reaction',
61
+ },
62
+ {
63
+ label: 'ART No started Opportunist Infection prophylaxis',
64
+ value: 'art_no_started_opportunist_infection_prophylaxis',
65
+ },
66
+ {
67
+ label: 'ART clients with new adverse clinical event',
68
+ value: 'art_clients_with_new_adverse_clinical_event',
69
+ },
70
+ {
71
+ label: 'ART defaulters',
72
+ value: 'art_defaulters',
73
+ },
74
+ {
75
+ label: 'ART enrollment stage 1',
76
+ value: 'art_enrollment_stage_1',
77
+ },
78
+ {
79
+ label: 'ART enrollment stage 2',
80
+ value: 'art_enrollment_stage_2',
81
+ },
82
+ {
83
+ label: 'ART enrollment stage 3',
84
+ value: 'art_enrollment_stage_3',
85
+ },
86
+ {
87
+ label: 'ART enrollment stage 4',
88
+ value: 'art_enrollment_stage_4',
89
+ },
90
+ {
91
+ label: 'ART entry point: No PMTCT',
92
+ value: 'art_entry_point:_no_pmtct',
93
+ },
94
+ ]
95
+
96
+ export const fiveOptions = options.slice(0, 5)
@@ -0,0 +1,70 @@
1
+ /* eslint-disable react/prop-types */
2
+ import { CustomDataProvider } from '@dhis2/app-runtime'
3
+ import React from 'react'
4
+ import { dataElements as allDataElements } from './dataElements.js'
5
+
6
+ function randomBetween(min, max) {
7
+ return Math.floor(Math.random() * (max - min + 1) + min)
8
+ }
9
+
10
+ const fakeNetworkDelay = ({ pager, dataElements, signal }) => {
11
+ const delay = randomBetween(750, 1550)
12
+ const response = { dataElements }
13
+ if (pager) {
14
+ response.pager = pager
15
+ }
16
+
17
+ return new Promise((resolve, reject) => {
18
+ const onAbort = () => {
19
+ clearTimeout(timeout)
20
+ reject('Aborted')
21
+ }
22
+
23
+ signal?.addEventListener('abort', onAbort)
24
+
25
+ const timeout = setTimeout(() => {
26
+ signal?.removeEventListener('abort', onAbort)
27
+ resolve(response)
28
+ }, delay)
29
+ })
30
+ }
31
+
32
+ const Provider = ({ children }) => {
33
+ const data = {
34
+ dataElements: (_, { id, params = {} }, { signal }) => {
35
+ if (id) {
36
+ const dataElement = allDataElements.find(
37
+ (curDE) => id === curDE.id
38
+ )
39
+ return fakeNetworkDelay({ dataElements: dataElement, signal })
40
+ }
41
+
42
+ // Only this specific filter is supported by this server-mock
43
+ const searchTerm = params.filter
44
+ ? params.filter[0].split(':')[2]
45
+ : ''
46
+
47
+ const filteredDataElements = searchTerm
48
+ ? allDataElements.filter(({ displayName }) =>
49
+ displayName.startsWith(searchTerm)
50
+ )
51
+ : allDataElements
52
+
53
+ const page = params.page || 1
54
+ const total = filteredDataElements.length
55
+ const pageSize = params.pageSize || 50
56
+ const pageCount = Math.ceil(total / pageSize)
57
+ const pager = { page, total, pageSize, pageCount }
58
+
59
+ const start = (page - 1) * pageSize
60
+ const end = start + pageSize
61
+ const dataElements = filteredDataElements.slice(start, end)
62
+
63
+ return fakeNetworkDelay({ pager, dataElements, signal })
64
+ },
65
+ }
66
+
67
+ return <CustomDataProvider data={data}>{children}</CustomDataProvider>
68
+ }
69
+
70
+ export { Provider as CustomDataProvider }