@dhis2-ui/select 10.16.1 → 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 (278) hide show
  1. package/build/cjs/select/select.js +5 -2
  2. package/build/cjs/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +2 -2
  3. package/build/es/select/select.js +5 -2
  4. package/build/es/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +2 -2
  5. package/package.json +17 -16
  6. package/src/index.js +9 -0
  7. package/src/locales/ar/translations.json +7 -0
  8. package/src/locales/ar_IQ/translations.json +7 -0
  9. package/src/locales/ckb/translations.json +7 -0
  10. package/src/locales/cs/translations.json +7 -0
  11. package/src/locales/da/translations.json +7 -0
  12. package/src/locales/en/translations.json +11 -0
  13. package/src/locales/en_US/translations.json +9 -0
  14. package/src/locales/es/translations.json +7 -0
  15. package/src/locales/es_419/translations.json +7 -0
  16. package/src/locales/fr/translations.json +7 -0
  17. package/src/locales/hi_IN/translations.json +7 -0
  18. package/src/locales/id/translations.json +7 -0
  19. package/src/locales/index.js +80 -0
  20. package/src/locales/km/translations.json +7 -0
  21. package/src/locales/lo/translations.json +7 -0
  22. package/src/locales/my/translations.json +7 -0
  23. package/src/locales/nb/translations.json +7 -0
  24. package/src/locales/nl/translations.json +7 -0
  25. package/src/locales/prs/translations.json +7 -0
  26. package/src/locales/ps/translations.json +7 -0
  27. package/src/locales/pt/translations.json +7 -0
  28. package/src/locales/pt_BR/translations.json +7 -0
  29. package/src/locales/ru/translations.json +7 -0
  30. package/src/locales/sv/translations.json +7 -0
  31. package/src/locales/tet/translations.json +7 -0
  32. package/src/locales/tg/translations.json +7 -0
  33. package/src/locales/uk/translations.json +7 -0
  34. package/src/locales/ur/translations.json +7 -0
  35. package/src/locales/uz_Latn/translations.json +7 -0
  36. package/src/locales/uz_UZ_Cyrl/translations.json +7 -0
  37. package/src/locales/uz_UZ_Latn/translations.json +7 -0
  38. package/src/locales/vi/translations.json +7 -0
  39. package/src/locales/zh/translations.json +9 -0
  40. package/src/locales/zh_CN/translations.json +7 -0
  41. package/src/multi-select/__tests__/multi-select.test.js +23 -0
  42. package/src/multi-select/__tests__/selection-list.test.js +102 -0
  43. package/src/multi-select/features/accepts_blur_cb/index.js +14 -0
  44. package/src/multi-select/features/accepts_blur_cb.feature +8 -0
  45. package/src/multi-select/features/accepts_focus_cb/index.js +14 -0
  46. package/src/multi-select/features/accepts_focus_cb.feature +6 -0
  47. package/src/multi-select/features/accepts_initial_focus/index.js +5 -0
  48. package/src/multi-select/features/accepts_initial_focus.feature +5 -0
  49. package/src/multi-select/features/accepts_loading/index.js +22 -0
  50. package/src/multi-select/features/accepts_loading.feature +14 -0
  51. package/src/multi-select/features/accepts_max_height/index.js +31 -0
  52. package/src/multi-select/features/accepts_max_height.feature +11 -0
  53. package/src/multi-select/features/accepts_placeholder/index.js +25 -0
  54. package/src/multi-select/features/accepts_placeholder.feature +10 -0
  55. package/src/multi-select/features/accepts_prefix/index.js +19 -0
  56. package/src/multi-select/features/accepts_prefix.feature +10 -0
  57. package/src/multi-select/features/allows_invalid_options/index.js +27 -0
  58. package/src/multi-select/features/allows_invalid_options.feature +12 -0
  59. package/src/multi-select/features/allows_selecting/index.js +81 -0
  60. package/src/multi-select/features/allows_selecting.feature +44 -0
  61. package/src/multi-select/features/can_be_cleared/index.js +22 -0
  62. package/src/multi-select/features/can_be_cleared.feature +6 -0
  63. package/src/multi-select/features/can_be_disabled/index.js +36 -0
  64. package/src/multi-select/features/can_be_disabled.feature +32 -0
  65. package/src/multi-select/features/can_be_empty/index.js +28 -0
  66. package/src/multi-select/features/can_be_empty.feature +16 -0
  67. package/src/multi-select/features/can_be_filtered/index.js +23 -0
  68. package/src/multi-select/features/can_be_filtered.feature +14 -0
  69. package/src/multi-select/features/can_be_opened_and_closed/index.js +32 -0
  70. package/src/multi-select/features/can_be_opened_and_closed.feature +41 -0
  71. package/src/multi-select/features/common/index.js +49 -0
  72. package/src/multi-select/features/duplicate_option_values/index.js +21 -0
  73. package/src/multi-select/features/duplicate_option_values.feature +7 -0
  74. package/src/multi-select/features/menu_width_matches_input/index.js +53 -0
  75. package/src/multi-select/features/menu_width_matches_input.feature +11 -0
  76. package/src/multi-select/features/position/index.js +160 -0
  77. package/src/multi-select/features/position.feature +46 -0
  78. package/src/multi-select/features/shows_selections/index.js +18 -0
  79. package/src/multi-select/features/shows_selections.feature +9 -0
  80. package/src/multi-select/filterable-menu.js +43 -0
  81. package/src/multi-select/index.js +1 -0
  82. package/src/multi-select/input.js +129 -0
  83. package/src/multi-select/menu.js +91 -0
  84. package/src/multi-select/multi-select.e2e.stories.js +407 -0
  85. package/src/multi-select/multi-select.js +148 -0
  86. package/src/multi-select/multi-select.prod.stories.js +408 -0
  87. package/src/multi-select/selection-list.js +79 -0
  88. package/src/multi-select-field/__tests__/multi-select-field.test.js +29 -0
  89. package/src/multi-select-field/features/accepts_help_text/index.js +9 -0
  90. package/src/multi-select-field/features/accepts_help_text.feature +5 -0
  91. package/src/multi-select-field/features/accepts_label/index.js +9 -0
  92. package/src/multi-select-field/features/accepts_label.feature +5 -0
  93. package/src/multi-select-field/features/accepts_validation_text/index.js +9 -0
  94. package/src/multi-select-field/features/accepts_validation_text.feature +5 -0
  95. package/src/multi-select-field/features/can_be_required/index.js +11 -0
  96. package/src/multi-select-field/features/can_be_required.feature +5 -0
  97. package/src/multi-select-field/features/has_default_clear_text/index.js +16 -0
  98. package/src/multi-select-field/features/has_default_clear_text.feature +6 -0
  99. package/src/multi-select-field/features/has_default_empty_text/index.js +13 -0
  100. package/src/multi-select-field/features/has_default_empty_text.feature +6 -0
  101. package/src/multi-select-field/features/has_default_filter_nomatch_text/index.js +17 -0
  102. package/src/multi-select-field/features/has_default_filter_nomatch_text.feature +7 -0
  103. package/src/multi-select-field/features/has_default_filter_placeholder/index.js +15 -0
  104. package/src/multi-select-field/features/has_default_filter_placeholder.feature +6 -0
  105. package/src/multi-select-field/features/has_default_loading_text/index.js +13 -0
  106. package/src/multi-select-field/features/has_default_loading_text.feature +6 -0
  107. package/src/multi-select-field/index.js +1 -0
  108. package/src/multi-select-field/multi-select-field.e2e.stories.js +45 -0
  109. package/src/multi-select-field/multi-select-field.js +178 -0
  110. package/src/multi-select-field/multi-select-field.prod.stories.js +137 -0
  111. package/src/multi-select-option/index.js +1 -0
  112. package/src/multi-select-option/multi-select-option.js +70 -0
  113. package/src/select/__tests__/select.test.js +31 -0
  114. package/src/select/debounce/debounce.js +30 -0
  115. package/src/select/debounce/debounce.spec.js +35 -0
  116. package/src/select/empty.js +28 -0
  117. package/src/select/filter-input.js +42 -0
  118. package/src/select/filterable-menu.js +106 -0
  119. package/src/select/index.js +14 -0
  120. package/src/select/input-clear-button.js +66 -0
  121. package/src/select/input-placeholder.js +30 -0
  122. package/src/select/input-prefix.js +32 -0
  123. package/src/select/input-wrapper.js +114 -0
  124. package/src/select/loading.js +33 -0
  125. package/src/select/menu-wrapper.js +52 -0
  126. package/src/select/no-match.js +28 -0
  127. package/src/select/option-helpers.js +34 -0
  128. package/src/select/select.js +256 -0
  129. package/src/simple-single-select/__stories__/DefaultPosition.js +37 -0
  130. package/src/simple-single-select/__stories__/Dense.js +17 -0
  131. package/src/simple-single-select/__stories__/Empty.js +15 -0
  132. package/src/simple-single-select/__stories__/EmptyWithEmptyNode.js +27 -0
  133. package/src/simple-single-select/__stories__/EmptyWithEmptyText.js +16 -0
  134. package/src/simple-single-select/__stories__/FlippedPosition.js +36 -0
  135. package/src/simple-single-select/__stories__/InfiniteLoading.js +65 -0
  136. package/src/simple-single-select/__stories__/Loading.js +15 -0
  137. package/src/simple-single-select/__stories__/ShiftedIntoView.js +35 -0
  138. package/src/simple-single-select/__stories__/WithClearButton.js +26 -0
  139. package/src/simple-single-select/__stories__/WithCustomLowMaxHeight.js +17 -0
  140. package/src/simple-single-select/__stories__/WithCustomOptions.js +78 -0
  141. package/src/simple-single-select/__stories__/WithDisabledOption.js +25 -0
  142. package/src/simple-single-select/__stories__/WithFilterField.js +30 -0
  143. package/src/simple-single-select/__stories__/WithInitialFocus.js +29 -0
  144. package/src/simple-single-select/__stories__/WithManyOptions.js +19 -0
  145. package/src/simple-single-select/__stories__/WithMultipleSelectsAndScrollableParent.js +65 -0
  146. package/src/simple-single-select/__stories__/WithNoMatchText.js +25 -0
  147. package/src/simple-single-select/__stories__/WithOnBlur.js +22 -0
  148. package/src/simple-single-select/__stories__/WithOnFocus.js +22 -0
  149. package/src/simple-single-select/__stories__/WithOptionsAndDisabled.js +17 -0
  150. package/src/simple-single-select/__stories__/WithOptionsAndLoading.js +23 -0
  151. package/src/simple-single-select/__stories__/WithOptionsAndLoadingText.js +24 -0
  152. package/src/simple-single-select/__stories__/WithPlaceholder.js +18 -0
  153. package/src/simple-single-select/__stories__/WithPlaceholderAndSelection.js +20 -0
  154. package/src/simple-single-select/__stories__/WithPrefix.js +17 -0
  155. package/src/simple-single-select/__stories__/WithPrefixAndSelection.js +20 -0
  156. package/src/simple-single-select/__stories__/WithRTL.js +29 -0
  157. package/src/simple-single-select/__stories__/WithScrollingParent.js +51 -0
  158. package/src/simple-single-select/__stories__/WithSelection.js +19 -0
  159. package/src/simple-single-select/__stories__/WithSelectionAndDisabled.js +20 -0
  160. package/src/simple-single-select/__stories__/WithServerSideFilteringDemo.js +245 -0
  161. package/src/simple-single-select/__stories__/WithSimpleFilteringDemo.js +50 -0
  162. package/src/simple-single-select/__stories__/WithoutOptionsAndLoading.js +16 -0
  163. package/src/simple-single-select/__stories__/WithoutSelection.js +17 -0
  164. package/src/simple-single-select/__stories__/options.js +96 -0
  165. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/CustomDataProvider.js +70 -0
  166. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/dataElements.js +4271 -0
  167. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/index.js +1 -0
  168. package/src/simple-single-select/features/keyboard-interactions.test.e2e.js +581 -0
  169. package/src/simple-single-select/features/menu-positioning.test.e2e.js +187 -0
  170. package/src/simple-single-select/index.js +2 -0
  171. package/src/simple-single-select/is-option-hidden.js +21 -0
  172. package/src/simple-single-select/menu/empty.js +27 -0
  173. package/src/simple-single-select/menu/filter.js +61 -0
  174. package/src/simple-single-select/menu/index.js +1 -0
  175. package/src/simple-single-select/menu/loading.js +38 -0
  176. package/src/simple-single-select/menu/menu.js +171 -0
  177. package/src/simple-single-select/menu/no-match.js +28 -0
  178. package/src/simple-single-select/menu/option.js +161 -0
  179. package/src/simple-single-select/menu/options-list.js +107 -0
  180. package/src/simple-single-select/selected-value/clear-button.js +71 -0
  181. package/src/simple-single-select/selected-value/container.js +146 -0
  182. package/src/simple-single-select/selected-value/index.js +1 -0
  183. package/src/simple-single-select/selected-value/placeholder.js +24 -0
  184. package/src/simple-single-select/selected-value/prefix.js +26 -0
  185. package/src/simple-single-select/selected-value/selected-value.js +174 -0
  186. package/src/simple-single-select/shared-prop-types.js +8 -0
  187. package/src/simple-single-select/simple-single-select.e2e.stories.js +216 -0
  188. package/src/simple-single-select/simple-single-select.js +389 -0
  189. package/src/simple-single-select/simple-single-select.prod.stories.js +41 -0
  190. package/src/simple-single-select/simple-single-select.test.js +1136 -0
  191. package/src/simple-single-select/use-handle-key-press/index.js +2 -0
  192. package/src/simple-single-select/use-handle-key-press/use-focus-option.js +24 -0
  193. package/src/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +259 -0
  194. package/src/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +147 -0
  195. package/src/simple-single-select/use-handle-key-press/use-handle-typing.js +79 -0
  196. package/src/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +62 -0
  197. package/src/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +38 -0
  198. package/src/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +60 -0
  199. package/src/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +52 -0
  200. package/src/simple-single-select/use-handle-key-press/use-page-down.js +50 -0
  201. package/src/simple-single-select/use-handle-key-press/use-page-up.js +50 -0
  202. package/src/simple-single-select/use-handle-key-press/utils.js +23 -0
  203. package/src/simple-single-select-field/index.js +1 -0
  204. package/src/simple-single-select-field/simple-single-select-field.js +198 -0
  205. package/src/simple-single-select-field/simple-single-select-field.prod.stories.js +188 -0
  206. package/src/simple-single-select-field/simple-single-select-field.test.js +144 -0
  207. package/src/single-select/__tests__/selection.test.js +102 -0
  208. package/src/single-select/__tests__/single-select.test.js +23 -0
  209. package/src/single-select/features/accepts_blur_cb/index.js +14 -0
  210. package/src/single-select/features/accepts_blur_cb.feature +8 -0
  211. package/src/single-select/features/accepts_focus_cb/index.js +14 -0
  212. package/src/single-select/features/accepts_focus_cb.feature +6 -0
  213. package/src/single-select/features/accepts_initial_focus/index.js +5 -0
  214. package/src/single-select/features/accepts_initial_focus.feature +5 -0
  215. package/src/single-select/features/accepts_loading/index.js +22 -0
  216. package/src/single-select/features/accepts_loading.feature +14 -0
  217. package/src/single-select/features/accepts_max_height/index.js +31 -0
  218. package/src/single-select/features/accepts_max_height.feature +11 -0
  219. package/src/single-select/features/accepts_placeholder/index.js +25 -0
  220. package/src/single-select/features/accepts_placeholder.feature +10 -0
  221. package/src/single-select/features/accepts_prefix/index.js +19 -0
  222. package/src/single-select/features/accepts_prefix.feature +10 -0
  223. package/src/single-select/features/allows_invalid_options/index.js +28 -0
  224. package/src/single-select/features/allows_invalid_options.feature +12 -0
  225. package/src/single-select/features/allows_selecting/index.js +38 -0
  226. package/src/single-select/features/allows_selecting.feature +21 -0
  227. package/src/single-select/features/can_be_cleared/index.js +22 -0
  228. package/src/single-select/features/can_be_cleared.feature +6 -0
  229. package/src/single-select/features/can_be_disabled/index.js +39 -0
  230. package/src/single-select/features/can_be_disabled.feature +32 -0
  231. package/src/single-select/features/can_be_empty/index.js +28 -0
  232. package/src/single-select/features/can_be_empty.feature +16 -0
  233. package/src/single-select/features/can_be_filtered/index.js +23 -0
  234. package/src/single-select/features/can_be_filtered.feature +14 -0
  235. package/src/single-select/features/can_be_opened_and_closed/index.js +32 -0
  236. package/src/single-select/features/can_be_opened_and_closed.feature +41 -0
  237. package/src/single-select/features/common/index.js +44 -0
  238. package/src/single-select/features/duplicate_option_values/index.js +26 -0
  239. package/src/single-select/features/duplicate_option_values.feature +7 -0
  240. package/src/single-select/features/menu_width_matches_input/index.js +53 -0
  241. package/src/single-select/features/menu_width_matches_input.feature +11 -0
  242. package/src/single-select/features/position/index.js +111 -0
  243. package/src/single-select/features/position.feature +26 -0
  244. package/src/single-select/features/shows_selections/index.js +9 -0
  245. package/src/single-select/features/shows_selections.feature +5 -0
  246. package/src/single-select/filterable-menu.js +41 -0
  247. package/src/single-select/index.js +1 -0
  248. package/src/single-select/input.js +101 -0
  249. package/src/single-select/menu.js +75 -0
  250. package/src/single-select/selection.js +60 -0
  251. package/src/single-select/single-select.e2e.stories.js +376 -0
  252. package/src/single-select/single-select.js +146 -0
  253. package/src/single-select/single-select.prod.stories.js +358 -0
  254. package/src/single-select-field/__tests__/single-select-field.test.js +29 -0
  255. package/src/single-select-field/features/accepts_help_text/index.js +9 -0
  256. package/src/single-select-field/features/accepts_help_text.feature +5 -0
  257. package/src/single-select-field/features/accepts_label/index.js +9 -0
  258. package/src/single-select-field/features/accepts_label.feature +5 -0
  259. package/src/single-select-field/features/accepts_validation_text/index.js +9 -0
  260. package/src/single-select-field/features/accepts_validation_text.feature +5 -0
  261. package/src/single-select-field/features/can_be_required/index.js +11 -0
  262. package/src/single-select-field/features/can_be_required.feature +5 -0
  263. package/src/single-select-field/features/has_default_clear_text/index.js +16 -0
  264. package/src/single-select-field/features/has_default_clear_text.feature +6 -0
  265. package/src/single-select-field/features/has_default_empty_text/index.js +13 -0
  266. package/src/single-select-field/features/has_default_empty_text.feature +6 -0
  267. package/src/single-select-field/features/has_default_filter_nomatch_text/index.js +17 -0
  268. package/src/single-select-field/features/has_default_filter_nomatch_text.feature +7 -0
  269. package/src/single-select-field/features/has_default_filter_placeholder/index.js +15 -0
  270. package/src/single-select-field/features/has_default_filter_placeholder.feature +6 -0
  271. package/src/single-select-field/features/has_default_loading_text/index.js +13 -0
  272. package/src/single-select-field/features/has_default_loading_text.feature +6 -0
  273. package/src/single-select-field/index.js +1 -0
  274. package/src/single-select-field/single-select-field.e2e.stories.js +45 -0
  275. package/src/single-select-field/single-select-field.js +178 -0
  276. package/src/single-select-field/single-select-field.prod.stories.js +146 -0
  277. package/src/single-select-option/index.js +1 -0
  278. package/src/single-select-option/single-select-option.js +84 -0
@@ -0,0 +1,161 @@
1
+ import { colors, spacers } from '@dhis2/ui-constants'
2
+ import cx from 'classnames'
3
+ import PropTypes from 'prop-types'
4
+ import React, { useEffect, useRef } from 'react'
5
+
6
+ const VISIBILE_INTERSECTION_RATIO = 0.99
7
+
8
+ function DefaultStyle({ label, disabled, highlighted }) {
9
+ return (
10
+ <span
11
+ className={cx('option', {
12
+ disabled,
13
+ active: highlighted,
14
+ })}
15
+ >
16
+ {label}
17
+
18
+ <style jsx>{`
19
+ span {
20
+ display: block;
21
+ cursor: pointer;
22
+ font-size: 14px;
23
+ text-decoration: none;
24
+ color: ${colors.grey900};
25
+ padding-block: ${spacers.dp8};
26
+ padding-inline: ${spacers.dp12};
27
+ line-height: ${spacers.dp16};
28
+ }
29
+
30
+ span:hover {
31
+ background-color: ${colors.grey200};
32
+ }
33
+
34
+ span:active,
35
+ span.active {
36
+ background-color: ${colors.teal700};
37
+ color: ${colors.white};
38
+ cursor: auto;
39
+ }
40
+
41
+ span.disabled {
42
+ color: ${colors.grey500};
43
+ cursor: not-allowed;
44
+ user-select: none;
45
+ }
46
+
47
+ span.disabled:hover {
48
+ background-color: initial;
49
+ }
50
+ `}</style>
51
+ </span>
52
+ )
53
+ }
54
+
55
+ DefaultStyle.propTypes = {
56
+ label: PropTypes.string.isRequired,
57
+ disabled: PropTypes.bool,
58
+ highlighted: PropTypes.bool,
59
+ }
60
+
61
+ export function Option({
62
+ comboBoxId,
63
+ index,
64
+ label,
65
+ value,
66
+ onClick,
67
+ component: StyleComponent = DefaultStyle,
68
+ dataTest,
69
+ disabled,
70
+ highlighted,
71
+ listBoxRef,
72
+ onBecameVisible,
73
+ ...rest
74
+ }) {
75
+ const buttonRef = useRef()
76
+
77
+ useEffect(() => {
78
+ if (onBecameVisible) {
79
+ const scrollableContainer = listBoxRef.current.parentNode
80
+
81
+ const intersectionOptions = {
82
+ root: scrollableContainer,
83
+ threshold: VISIBILE_INTERSECTION_RATIO,
84
+ }
85
+
86
+ const intersectionHandler = (entries) => {
87
+ entries.forEach((result) => {
88
+ const { intersectionRatio } = result
89
+ if (intersectionRatio >= VISIBILE_INTERSECTION_RATIO) {
90
+ onBecameVisible()
91
+ }
92
+ })
93
+ }
94
+
95
+ const observer = new IntersectionObserver(
96
+ intersectionHandler,
97
+ intersectionOptions
98
+ )
99
+
100
+ observer.observe(buttonRef.current)
101
+ return () => observer.disconnect()
102
+ }
103
+ }, [onBecameVisible, listBoxRef])
104
+
105
+ return (
106
+ <button
107
+ ref={buttonRef}
108
+ id={`${comboBoxId}-${index}`}
109
+ data-test={dataTest}
110
+ disabled={disabled}
111
+ role="option"
112
+ aria-selected={highlighted || 'false'}
113
+ aria-disabled={disabled}
114
+ aria-label={label}
115
+ onClick={() => {
116
+ if (!disabled) {
117
+ onClick({ value, label })
118
+ }
119
+ }}
120
+ >
121
+ <StyleComponent
122
+ value={value}
123
+ label={label}
124
+ index={index}
125
+ disabled={disabled}
126
+ highlighted={highlighted}
127
+ {...rest}
128
+ >
129
+ {label}
130
+ </StyleComponent>
131
+
132
+ <style jsx>{`
133
+ button {
134
+ display: block;
135
+ width: 100%;
136
+ padding: 0;
137
+ border: 0;
138
+ background: 0;
139
+ outline: 0;
140
+ text-align: start;
141
+ }
142
+ `}</style>
143
+ </button>
144
+ )
145
+ }
146
+
147
+ Option.propTypes = {
148
+ comboBoxId: PropTypes.string.isRequired,
149
+ index: PropTypes.number.isRequired,
150
+ label: PropTypes.string.isRequired,
151
+ value: PropTypes.string.isRequired,
152
+ onClick: PropTypes.func.isRequired,
153
+ component: PropTypes.elementType,
154
+ dataTest: PropTypes.string,
155
+ disabled: PropTypes.bool,
156
+ highlighted: PropTypes.bool,
157
+ listBoxRef: PropTypes.shape({
158
+ current: PropTypes.instanceOf(HTMLElement),
159
+ }),
160
+ onBecameVisible: PropTypes.func,
161
+ }
@@ -0,0 +1,107 @@
1
+ import PropTypes from 'prop-types'
2
+ import React, { forwardRef, useEffect } from 'react'
3
+ import { isOptionHidden } from '../is-option-hidden.js'
4
+ import { optionProp } from '../shared-prop-types.js'
5
+ import { Option } from './option.js'
6
+
7
+ export const OptionsList = forwardRef(function OptionsList(
8
+ {
9
+ comboBoxId,
10
+ optionComponent,
11
+ focussedOptionIndex,
12
+ id,
13
+ labelledBy,
14
+ optionUpdateStrategy,
15
+ options,
16
+ selectedValue,
17
+ dataTest,
18
+ disabled,
19
+ loading,
20
+ onChange,
21
+ onBlur,
22
+ onEndReached,
23
+ },
24
+ ref
25
+ ) {
26
+ // scrolls the highlighted option into view when:
27
+ // * the highlighted option changes
28
+ // * the menu opens
29
+ useEffect(() => {
30
+ const { current: listBox } = ref
31
+ const highlightedOption = listBox.childNodes[focussedOptionIndex]
32
+
33
+ if (highlightedOption) {
34
+ const listBoxParent = listBox.parentNode
35
+ const optionHidden = isOptionHidden(
36
+ highlightedOption,
37
+ listBoxParent
38
+ )
39
+
40
+ if (optionHidden) {
41
+ highlightedOption.scrollIntoView()
42
+ }
43
+ }
44
+ }, [focussedOptionIndex, ref])
45
+
46
+ return (
47
+ <div
48
+ ref={ref}
49
+ role="listbox"
50
+ id={id}
51
+ aria-labelledby={labelledBy}
52
+ aria-live={optionUpdateStrategy}
53
+ aria-busy={loading.toString()}
54
+ data-test={dataTest}
55
+ onBlur={onBlur}
56
+ >
57
+ {options.map(
58
+ (
59
+ {
60
+ value,
61
+ label,
62
+ component,
63
+ disabled: optionDisabled = false,
64
+ },
65
+ index
66
+ ) => {
67
+ const isSelected = value === selectedValue
68
+ const isLast = index === options.length - 1
69
+
70
+ return (
71
+ <Option
72
+ listBoxRef={ref}
73
+ dataTest={`${dataTest}-option`}
74
+ highlighted={focussedOptionIndex === index}
75
+ key={value}
76
+ value={value}
77
+ label={label}
78
+ index={index}
79
+ comboBoxId={comboBoxId}
80
+ disabled={disabled || optionDisabled}
81
+ onClick={isSelected ? () => null : onChange}
82
+ component={component || optionComponent}
83
+ onBecameVisible={isLast ? onEndReached : undefined}
84
+ />
85
+ )
86
+ }
87
+ )}
88
+ </div>
89
+ )
90
+ })
91
+
92
+ OptionsList.propTypes = {
93
+ comboBoxId: PropTypes.string.isRequired,
94
+ focussedOptionIndex: PropTypes.number.isRequired,
95
+ id: PropTypes.string.isRequired,
96
+ options: PropTypes.arrayOf(optionProp).isRequired,
97
+ onChange: PropTypes.func.isRequired,
98
+ dataTest: PropTypes.string,
99
+ disabled: PropTypes.bool,
100
+ labelledBy: PropTypes.string,
101
+ loading: PropTypes.bool,
102
+ optionComponent: PropTypes.elementType,
103
+ optionUpdateStrategy: PropTypes.oneOf(['off', 'polite', 'assertive']),
104
+ selectedValue: PropTypes.string,
105
+ onBlur: PropTypes.func,
106
+ onEndReached: PropTypes.func,
107
+ }
@@ -0,0 +1,71 @@
1
+ import { colors, theme } from '@dhis2/ui-constants'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+ import i18n from '../../locales/index.js'
5
+
6
+ function ClearButton({ onClear, clearText, dataTest }) {
7
+ return (
8
+ <button
9
+ aria-label={i18n.t('{{clearText}}', { clearText })}
10
+ title={i18n.t('{{clearText}}', { clearText })}
11
+ data-test={dataTest}
12
+ onKeyDown={(e) => {
13
+ if (e.key === 'Enter' || e.key === 'space') {
14
+ // otherwise, Enter propagates to parent menu and opens the combo
15
+ e.stopPropagation()
16
+ }
17
+ }}
18
+ onClick={(e) => {
19
+ e.stopPropagation()
20
+ onClear()
21
+ }}
22
+ tabIndex="-1"
23
+ >
24
+ <svg
25
+ width="16"
26
+ height="16"
27
+ viewBox="0 0 16 16"
28
+ fill="none"
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ >
31
+ <path
32
+ d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16Zm3.536-5.879a1 1 0 1 1-1.415 1.414l-2.12-2.12-2.122 2.121a1 1 0 1 1-1.415-1.414L6.586 8 4.464 5.878A1 1 0 1 1 5.88 4.464L8 6.586l2.121-2.121a1 1 0 1 1 1.415 1.414L9.415 8l2.12 2.121Z"
33
+ fill="none"
34
+ />
35
+ </svg>
36
+ <style jsx>{`
37
+ button {
38
+ background: none;
39
+ border: none;
40
+ display: flex;
41
+ align-items: center;
42
+ justify-content: center;
43
+ cursor: pointer;
44
+ height: 24px;
45
+ width: 24px;
46
+ border-radius: 3px;
47
+ }
48
+ button svg path {
49
+ fill: ${colors.grey500};
50
+ }
51
+ button:hover svg path {
52
+ fill: ${colors.grey800};
53
+ }
54
+ button:hover {
55
+ background: ${colors.grey200};
56
+ }
57
+ button:focus {
58
+ outline: 2px solid ${theme.focus};
59
+ }
60
+ `}</style>
61
+ </button>
62
+ )
63
+ }
64
+
65
+ ClearButton.propTypes = {
66
+ clearText: PropTypes.string.isRequired,
67
+ onClear: PropTypes.func.isRequired,
68
+ dataTest: PropTypes.string,
69
+ }
70
+
71
+ export { ClearButton }
@@ -0,0 +1,146 @@
1
+ import { colors, theme } from '@dhis2/ui-constants'
2
+ import cx from 'classnames'
3
+ import PropTypes from 'prop-types'
4
+ import React, { forwardRef, useCallback, useEffect } from 'react'
5
+
6
+ export const Container = forwardRef(function Container(
7
+ {
8
+ children,
9
+ comboBoxId,
10
+ autoFocus,
11
+ dataTest,
12
+ dense,
13
+ disabled,
14
+ error,
15
+ expanded,
16
+ labelledBy,
17
+ name,
18
+ placeholder,
19
+ tabIndex,
20
+ valid,
21
+ warning,
22
+ onBlur,
23
+ onClick: _onClick,
24
+ onFocus,
25
+ onKeyDown,
26
+ },
27
+ ref
28
+ ) {
29
+ // Using useEffect so we have access to the ref, which will be set before
30
+ // the first call to the useEffect's callback
31
+ useEffect(
32
+ () => {
33
+ if (autoFocus) {
34
+ ref.current.focus()
35
+ }
36
+ },
37
+ // We want to run this only once:
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
39
+ []
40
+ )
41
+
42
+ const onClick = useCallback(
43
+ (...args) => {
44
+ if (!disabled) {
45
+ _onClick(...args)
46
+ }
47
+ },
48
+ [disabled, _onClick]
49
+ )
50
+
51
+ return (
52
+ <div
53
+ className={cx({ error, warning, valid, disabled, dense })}
54
+ data-test={dataTest}
55
+ ref={ref}
56
+ aria-controls={`${name}-listbox`}
57
+ aria-expanded={expanded.toString()}
58
+ aria-haspopup="listbox"
59
+ aria-labelledby={labelledBy}
60
+ aria-placeholder={placeholder}
61
+ id={comboBoxId}
62
+ role="combobox"
63
+ tabIndex={tabIndex}
64
+ onFocus={onFocus}
65
+ onBlur={onBlur}
66
+ onClick={onClick}
67
+ onKeyDown={(e) => {
68
+ e.stopPropagation()
69
+ onKeyDown(e)
70
+ }}
71
+ >
72
+ {children}
73
+
74
+ <style jsx>{`
75
+ div {
76
+ background-color: white;
77
+ border-radius: 3px;
78
+ border: 1px solid ${colors.grey500};
79
+ box-sizing: border-box;
80
+ min-height: 40px;
81
+ padding-block: 6px;
82
+ padding-inline: 12px;
83
+ box-shadow: inset 0 0 1px 0 rgba(48, 54, 60, 0.1);
84
+ display: flex;
85
+ align-items: center;
86
+ color: ${colors.grey900};
87
+ font-size: 14px;
88
+ line-height: 16px;
89
+ }
90
+
91
+ div:not(.disabled):focus,
92
+ div:not(.disabled):active {
93
+ outline: none;
94
+ box-shadow: inset 0 0 0 2px ${theme.focus};
95
+ border-color: ${theme.focus};
96
+ }
97
+
98
+ div.valid {
99
+ border-color: ${theme.valid};
100
+ }
101
+
102
+ div.warning {
103
+ border-color: ${theme.warning};
104
+ }
105
+
106
+ div.error {
107
+ border-color: ${theme.error};
108
+ }
109
+
110
+ div.disabled {
111
+ background-color: ${colors.grey100};
112
+ border-color: ${colors.grey500};
113
+ color: ${theme.disabled};
114
+ cursor: not-allowed;
115
+ }
116
+
117
+ div.dense {
118
+ padding-block: 2px;
119
+ padding-inline: 8px;
120
+ min-height: 32px;
121
+ }
122
+ `}</style>
123
+ </div>
124
+ )
125
+ })
126
+
127
+ Container.propTypes = {
128
+ children: PropTypes.any.isRequired,
129
+ comboBoxId: PropTypes.string.isRequired,
130
+ name: PropTypes.string.isRequired,
131
+ onKeyDown: PropTypes.func.isRequired,
132
+ autoFocus: PropTypes.bool,
133
+ dataTest: PropTypes.string,
134
+ dense: PropTypes.bool,
135
+ disabled: PropTypes.bool,
136
+ error: PropTypes.bool,
137
+ expanded: PropTypes.bool,
138
+ labelledBy: PropTypes.string,
139
+ placeholder: PropTypes.string,
140
+ tabIndex: PropTypes.string,
141
+ valid: PropTypes.bool,
142
+ warning: PropTypes.bool,
143
+ onBlur: PropTypes.func,
144
+ onClick: PropTypes.func,
145
+ onFocus: PropTypes.func,
146
+ }
@@ -0,0 +1 @@
1
+ export { SelectedValue } from './selected-value.js'
@@ -0,0 +1,24 @@
1
+ import { colors } from '@dhis2/ui-constants'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+
5
+ export function Placeholder({ placeholder, className, dataTest }) {
6
+ return (
7
+ <div className={className} data-test={dataTest}>
8
+ {placeholder}
9
+
10
+ <style jsx>{`
11
+ div {
12
+ color: ${colors.grey600};
13
+ user-select: none;
14
+ }
15
+ `}</style>
16
+ </div>
17
+ )
18
+ }
19
+
20
+ Placeholder.propTypes = {
21
+ dataTest: PropTypes.string.isRequired,
22
+ className: PropTypes.string,
23
+ placeholder: PropTypes.string,
24
+ }
@@ -0,0 +1,26 @@
1
+ import { colors, spacers } from '@dhis2/ui-constants'
2
+ import PropTypes from 'prop-types'
3
+ import React from 'react'
4
+
5
+ export function Prefix({ prefix, className, dataTest }) {
6
+ return (
7
+ <div className={className} data-test={dataTest}>
8
+ {prefix}
9
+
10
+ <style jsx>{`
11
+ div {
12
+ color: ${colors.grey600};
13
+ padding-inline-end: ${spacers.dp12};
14
+ font-size: 14px;
15
+ user-select: none;
16
+ }
17
+ `}</style>
18
+ </div>
19
+ )
20
+ }
21
+
22
+ Prefix.propTypes = {
23
+ dataTest: PropTypes.string.isRequired,
24
+ className: PropTypes.string,
25
+ prefix: PropTypes.string,
26
+ }