@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,75 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+ import { Empty, filterIgnored, checkIfValidOption } from '../select/index.js'
4
+
5
+ const onIgnoredClick = (_, e) => {
6
+ e.stopPropagation()
7
+ e.preventDefault()
8
+ }
9
+
10
+ const Menu = ({
11
+ options,
12
+ onChange,
13
+ selected,
14
+ empty = '',
15
+ handleFocusInput,
16
+ handleClose,
17
+ dataTest,
18
+ }) => {
19
+ const renderedOptions = filterIgnored(options)
20
+
21
+ if (React.Children.count(renderedOptions) === 0) {
22
+ // If it's a string, supply it to our <Empty> component so it looks better
23
+ if (typeof empty === 'string') {
24
+ return <Empty message={empty} dataTest={`${dataTest}-empty`} />
25
+ }
26
+
27
+ // Otherwise just render the supplied markup
28
+ return empty
29
+ }
30
+
31
+ const children = React.Children.map(options, (child) => {
32
+ const isValidOption = checkIfValidOption(child)
33
+
34
+ // Return early if the child isn't an option, to prevent attaching handlers etc.
35
+ if (!isValidOption) {
36
+ return child
37
+ }
38
+
39
+ const { value, disabled: isDisabled } = child.props
40
+
41
+ // Active means the option is currently selected
42
+ const isActive = value === selected
43
+ const onClick = (_, e) => {
44
+ const data = { selected: value }
45
+ e.stopPropagation()
46
+
47
+ onChange(data, e)
48
+ handleClose()
49
+ handleFocusInput()
50
+ }
51
+
52
+ // Clicks on active options or disabled options should be ignored for the single select
53
+ const isIgnored = isActive || isDisabled
54
+
55
+ return React.cloneElement(child, {
56
+ ...child.props,
57
+ onClick: isIgnored ? onIgnoredClick : onClick,
58
+ active: isActive,
59
+ })
60
+ })
61
+
62
+ return <React.Fragment>{children}</React.Fragment>
63
+ }
64
+
65
+ Menu.propTypes = {
66
+ dataTest: PropTypes.string.isRequired,
67
+ empty: PropTypes.node,
68
+ handleClose: PropTypes.func,
69
+ handleFocusInput: PropTypes.func,
70
+ options: PropTypes.node,
71
+ selected: PropTypes.string,
72
+ onChange: PropTypes.func,
73
+ }
74
+
75
+ export { Menu }
@@ -0,0 +1,60 @@
1
+ import { spacers } from '@dhis2/ui-constants'
2
+ import cx from 'classnames'
3
+ import PropTypes from 'prop-types'
4
+ import React from 'react'
5
+ import { findOptionChild } from '../select/index.js'
6
+
7
+ const Selection = ({ options, selected, className }) => {
8
+ const isProduction = process.env.NODE_ENV === 'production'
9
+ const selectedOption = findOptionChild(selected, options)
10
+
11
+ if (!selectedOption) {
12
+ const message =
13
+ `There is no option with the value: "${selected}". ` +
14
+ 'Make sure that the value passed to the selected ' +
15
+ 'prop matches the value of an existing option.'
16
+
17
+ if (isProduction) {
18
+ // Don't crash the app if in production
19
+ console.error(message)
20
+ } else {
21
+ // Throw error if not in production for maximum visibility
22
+ throw new Error(message)
23
+ }
24
+ }
25
+
26
+ const icon = selectedOption && selectedOption.props.icon
27
+ // Use the selected value if we do not have a label
28
+ const label = selectedOption ? selectedOption.props.label : selected
29
+
30
+ return (
31
+ <div className={cx(className, 'root')}>
32
+ {icon && <div className="root-icon">{icon}</div>}
33
+ {label}
34
+
35
+ <style jsx>{`
36
+ .root {
37
+ display: flex;
38
+ align-items: center;
39
+ user-select: none;
40
+ }
41
+
42
+ .root-icon {
43
+ margin-inline-end: ${spacers.dp8};
44
+ width: ${spacers.dp16};
45
+ height: ${spacers.dp16};
46
+ overflow: hidden;
47
+ flex-shrink: 0;
48
+ }
49
+ `}</style>
50
+ </div>
51
+ )
52
+ }
53
+
54
+ Selection.propTypes = {
55
+ className: PropTypes.string,
56
+ options: PropTypes.node,
57
+ selected: PropTypes.string,
58
+ }
59
+
60
+ export { Selection }
@@ -0,0 +1,376 @@
1
+ import { Button } from '@dhis2-ui/button'
2
+ import { Modal, ModalTitle, ModalContent } from '@dhis2-ui/modal'
3
+ import PropTypes from 'prop-types'
4
+ import React, { useState } from 'react'
5
+ import { SingleSelectOption } from '../index.js'
6
+ import { SingleSelect } from './index.js'
7
+
8
+ window.onChange = window.Cypress && window.Cypress.cy.stub()
9
+ window.onFocus = window.Cypress && window.Cypress.cy.stub()
10
+ window.onBlur = window.Cypress && window.Cypress.cy.stub()
11
+
12
+ const CustomSingleSelectOption = ({ label, onClick }) => (
13
+ <div onClick={(e) => onClick({}, e)}>{label}</div>
14
+ )
15
+
16
+ CustomSingleSelectOption.propTypes = {
17
+ label: PropTypes.string,
18
+ onClick: PropTypes.func,
19
+ }
20
+
21
+ const options = [
22
+ { name: 'Hello', id: '1' },
23
+ { name: 'world', id: '2' },
24
+ { name: 'foo', id: '3' },
25
+ { name: 'bar', id: '4' },
26
+ ]
27
+
28
+ export default { title: 'SingleSelect' }
29
+ export const WithOptions = () => (
30
+ <SingleSelect className="select">
31
+ <SingleSelectOption value="1" label="option one" />
32
+ <SingleSelectOption value="2" label="option two" />
33
+ <SingleSelectOption value="3" label="option three" />
34
+ </SingleSelect>
35
+ )
36
+ export const WithOptionsAndOnChange = () => (
37
+ <SingleSelect className="select" onChange={window.onChange}>
38
+ <SingleSelectOption value="1" label="option one" />
39
+ <SingleSelectOption value="2" label="option two" />
40
+ <SingleSelectOption value="3" label="option three" />
41
+ </SingleSelect>
42
+ )
43
+ export const WithOnFocus = () => (
44
+ <SingleSelect className="select" onFocus={window.onFocus}>
45
+ <SingleSelectOption value="1" label="option one" />
46
+ <SingleSelectOption value="2" label="option two" />
47
+ <SingleSelectOption value="3" label="option three" />
48
+ </SingleSelect>
49
+ )
50
+ export const WithOnBlur = () => (
51
+ <SingleSelect className="select" onBlur={window.onBlur}>
52
+ <SingleSelectOption value="1" label="option one" />
53
+ <SingleSelectOption value="2" label="option two" />
54
+ <SingleSelectOption value="3" label="option three" />
55
+ </SingleSelect>
56
+ )
57
+ export const WithCustomOptionsAndOnChange = () => (
58
+ <SingleSelect className="select" onChange={window.onChange}>
59
+ <CustomSingleSelectOption value="1" label="option one" />
60
+ <CustomSingleSelectOption value="2" label="option two" />
61
+ <CustomSingleSelectOption value="3" label="option three" />
62
+ </SingleSelect>
63
+ )
64
+ export const WithInvalidOptions = () => (
65
+ <SingleSelect className="select">
66
+ <div>invalid one</div>
67
+ <SingleSelectOption value="1" label="option one" />
68
+ <div>invalid two</div>
69
+ <SingleSelectOption value="2" label="option two" />
70
+ <div>invalid three</div>
71
+ <SingleSelectOption value="3" label="option three" />
72
+ {null}
73
+ {undefined}
74
+ {false}
75
+ </SingleSelect>
76
+ )
77
+ export const WithInvalidFilterableOptions = () => (
78
+ <SingleSelect filterable className="select" noMatchText="No match">
79
+ <div>invalid one</div>
80
+ <SingleSelectOption value="1" label="option one" />
81
+ <div>invalid two</div>
82
+ <SingleSelectOption value="2" label="option two" />
83
+ <div>invalid three</div>
84
+ <SingleSelectOption value="3" label="option three" />
85
+ </SingleSelect>
86
+ )
87
+ export const WithInitialFocus = () => (
88
+ <SingleSelect className="select" initialFocus />
89
+ )
90
+ export const Empty = () => <SingleSelect className="select" />
91
+ export const EmptyWithEmptyText = () => (
92
+ <SingleSelect className="select" empty="Custom empty text" />
93
+ )
94
+ export const EmptyWithEmptyComponent = () => (
95
+ <SingleSelect
96
+ className="select"
97
+ empty={<div className="custom-empty">Custom empty component</div>}
98
+ />
99
+ )
100
+ export const WithOptionsAndLoading = () => (
101
+ <SingleSelect className="select" loading>
102
+ <SingleSelectOption value="1" label="option one" />
103
+ <SingleSelectOption value="2" label="option two" />
104
+ <SingleSelectOption value="3" label="option three" />
105
+ </SingleSelect>
106
+ )
107
+ export const WithOptionsLoadingAndLoadingText = () => (
108
+ <SingleSelect className="select" loadingText="Loading options" loading>
109
+ <SingleSelectOption value="1" label="option one" />
110
+ <SingleSelectOption value="2" label="option two" />
111
+ <SingleSelectOption value="3" label="option three" />
112
+ </SingleSelect>
113
+ )
114
+ export const WithMoreThanTenOptions = () => (
115
+ <SingleSelect className="select">
116
+ <SingleSelectOption value="1" label="option one" />
117
+ <SingleSelectOption value="2" label="option two" />
118
+ <SingleSelectOption value="3" label="option three" />
119
+ <SingleSelectOption value="4" label="option four" />
120
+ <SingleSelectOption value="5" label="option five" />
121
+ <SingleSelectOption value="6" label="option six" />
122
+ <SingleSelectOption value="7" label="option seven" />
123
+ <SingleSelectOption value="8" label="option eight" />
124
+ <SingleSelectOption value="9" label="option nine" />
125
+ <SingleSelectOption value="10" label="option ten" />
126
+ <SingleSelectOption value="11" label="option eleven" />
127
+ <SingleSelectOption value="12" label="option twelve" />
128
+ </SingleSelect>
129
+ )
130
+ export const WithMoreThanThreeOptionsAndA100pxMaxHeight = () => (
131
+ <SingleSelect className="select" maxHeight="100px">
132
+ <SingleSelectOption value="1" label="option one" />
133
+ <SingleSelectOption value="2" label="option two" />
134
+ <SingleSelectOption value="3" label="option three" />
135
+ <SingleSelectOption value="4" label="option four" />
136
+ <SingleSelectOption value="5" label="option five" />
137
+ <SingleSelectOption value="6" label="option six" />
138
+ <SingleSelectOption value="7" label="option seven" />
139
+ <SingleSelectOption value="8" label="option eight" />
140
+ <SingleSelectOption value="9" label="option nine" />
141
+ <SingleSelectOption value="10" label="option ten" />
142
+ <SingleSelectOption value="11" label="option eleven" />
143
+ <SingleSelectOption value="12" label="option twelve" />
144
+ </SingleSelect>
145
+ )
146
+ export const WithOptionsASelectionAndDisabled = () => (
147
+ <SingleSelect disabled className="select" selected="1">
148
+ <SingleSelectOption value="1" label="option one" />
149
+ <SingleSelectOption value="2" label="option two" />
150
+ <SingleSelectOption value="3" label="option three" />
151
+ </SingleSelect>
152
+ )
153
+ export const WithOptionsAndDisabled = () => (
154
+ <SingleSelect disabled className="select">
155
+ <SingleSelectOption value="1" label="option one" />
156
+ <SingleSelectOption value="2" label="option two" />
157
+ <SingleSelectOption value="3" label="option three" />
158
+ </SingleSelect>
159
+ )
160
+ export const WithPrefix = () => (
161
+ <SingleSelect className="select" prefix="Prefix text">
162
+ <SingleSelectOption value="1" label="option one" />
163
+ <SingleSelectOption value="2" label="option two" />
164
+ <SingleSelectOption value="3" label="option three" />
165
+ </SingleSelect>
166
+ )
167
+ export const WithPrefixAndSelection = () => (
168
+ <SingleSelect className="select" prefix="Prefix text" selected="1">
169
+ <SingleSelectOption value="1" label="option one" />
170
+ <SingleSelectOption value="2" label="option two" />
171
+ <SingleSelectOption value="3" label="option three" />
172
+ </SingleSelect>
173
+ )
174
+ export const WithPlaceholder = () => (
175
+ <SingleSelect className="select" placeholder="Placeholder text">
176
+ <SingleSelectOption value="1" label="option one" />
177
+ <SingleSelectOption value="2" label="option two" />
178
+ <SingleSelectOption value="3" label="option three" />
179
+ </SingleSelect>
180
+ )
181
+ export const WithPlaceholderAndSelection = () => (
182
+ <SingleSelect
183
+ className="select"
184
+ selected="1"
185
+ placeholder="Placeholder text"
186
+ >
187
+ <SingleSelectOption value="1" label="option one" />
188
+ <SingleSelectOption value="2" label="option two" />
189
+ <SingleSelectOption value="3" label="option three" />
190
+ </SingleSelect>
191
+ )
192
+ export const WithDisabledOptionAndOnChange = () => (
193
+ <SingleSelect className="select" onChange={window.onChange}>
194
+ <SingleSelectOption value="1" label="option one" />
195
+ <SingleSelectOption value="2" label="option two" />
196
+ <SingleSelectOption value="3" label="option three" />
197
+ <SingleSelectOption value="4" label="disabled option" disabled />
198
+ </SingleSelect>
199
+ )
200
+ export const WithOptionsAndASelection = () => (
201
+ <SingleSelect className="select" selected="1">
202
+ <SingleSelectOption value="1" label="option one" />
203
+ <SingleSelectOption value="2" label="option two" />
204
+ <SingleSelectOption value="3" label="option three" />
205
+ </SingleSelect>
206
+ )
207
+ export const WithOptionsASelectionAndOnChange = () => (
208
+ <SingleSelect className="select" selected="1" onChange={window.onChange}>
209
+ <SingleSelectOption value="1" label="option one" />
210
+ <SingleSelectOption value="2" label="option two" />
211
+ <SingleSelectOption value="3" label="option three" />
212
+ </SingleSelect>
213
+ )
214
+ export const WithClearButtonSelectionAndOnChange = () => (
215
+ <SingleSelect
216
+ clearable
217
+ clearText="Clear"
218
+ className="select"
219
+ selected="1"
220
+ onChange={window.onChange}
221
+ >
222
+ <SingleSelectOption value="1" label="option one" />
223
+ <SingleSelectOption value="2" label="option two" />
224
+ <SingleSelectOption value="3" label="option three" />
225
+ </SingleSelect>
226
+ )
227
+ export const WithFilterField = () => (
228
+ <SingleSelect filterable noMatchText="No match found" className="select">
229
+ <SingleSelectOption value="1" label="option one" />
230
+ <SingleSelectOption value="2" label="option two" />
231
+ <SingleSelectOption value="3" label="option three" />
232
+ </SingleSelect>
233
+ )
234
+ export const DefaultPosition = () => (
235
+ <>
236
+ <SingleSelect className="select">
237
+ <SingleSelectOption value="1" label="option one" />
238
+ <SingleSelectOption value="2" label="option two" />
239
+ <SingleSelectOption value="3" label="option three" />
240
+ <SingleSelectOption value="4" label="option four" />
241
+ <SingleSelectOption value="5" label="option five" />
242
+ <SingleSelectOption value="6" label="option six" />
243
+ <SingleSelectOption value="7" label="option seven" />
244
+ <SingleSelectOption value="8" label="option eight" />
245
+ <SingleSelectOption value="9" label="option nine" />
246
+ <SingleSelectOption value="10" label="option ten" />
247
+ <SingleSelectOption value="11" label="option eleven" />
248
+ <SingleSelectOption value="12" label="option twelve" />
249
+ </SingleSelect>
250
+ <style jsx>{`
251
+ :global(#storybook-root) {
252
+ margin-bottom: 2000px;
253
+ }
254
+ `}</style>
255
+ </>
256
+ )
257
+ export const FlippedPosition = () => (
258
+ <>
259
+ <SingleSelect className="select">
260
+ <SingleSelectOption value="1" label="option one" />
261
+ <SingleSelectOption value="2" label="option two" />
262
+ <SingleSelectOption value="3" label="option three" />
263
+ <SingleSelectOption value="4" label="option four" />
264
+ <SingleSelectOption value="5" label="option five" />
265
+ <SingleSelectOption value="6" label="option six" />
266
+ <SingleSelectOption value="7" label="option seven" />
267
+ <SingleSelectOption value="8" label="option eight" />
268
+ <SingleSelectOption value="9" label="option nine" />
269
+ <SingleSelectOption value="10" label="option ten" />
270
+ <SingleSelectOption value="11" label="option eleven" />
271
+ <SingleSelectOption value="12" label="option twelve" />
272
+ </SingleSelect>
273
+ <style jsx>{`
274
+ :global(html),
275
+ :global(body),
276
+ :global(#storybook-root) {
277
+ position: relative;
278
+ height: 500px;
279
+ max-height: 500px;
280
+ }
281
+ :global(#storybook-root) {
282
+ padding-top: 400px !important;
283
+ }
284
+ `}</style>
285
+ </>
286
+ )
287
+ export const ShiftedIntoView = () => (
288
+ <>
289
+ <SingleSelect className="select">
290
+ <SingleSelectOption value="1" label="option one" />
291
+ <SingleSelectOption value="2" label="option two" />
292
+ <SingleSelectOption value="3" label="option three" />
293
+ <SingleSelectOption value="4" label="option four" />
294
+ <SingleSelectOption value="5" label="option five" />
295
+ <SingleSelectOption value="6" label="option six" />
296
+ <SingleSelectOption value="7" label="option seven" />
297
+ <SingleSelectOption value="8" label="option eight" />
298
+ <SingleSelectOption value="9" label="option nine" />
299
+ <SingleSelectOption value="10" label="option ten" />
300
+ <SingleSelectOption value="11" label="option eleven" />
301
+ <SingleSelectOption value="12" label="option twelve" />
302
+ </SingleSelect>
303
+ <style jsx>{`
304
+ :global(html),
305
+ :global(body),
306
+ :global(#storybook-root) {
307
+ position: relative;
308
+ height: 300px !important;
309
+ max-height: 300px;
310
+ }
311
+ :global(#storybook-root) {
312
+ padding-top: 130px !important;
313
+ }
314
+ `}</style>
315
+ </>
316
+ )
317
+ export const WithDuplicateSelectedOptionValues = () => (
318
+ <SingleSelect className="select" selected="1">
319
+ <SingleSelectOption value="1" label="option one" />
320
+ <SingleSelectOption value="1" label="option one a" />
321
+ <SingleSelectOption value="2" label="option two" />
322
+ <SingleSelectOption value="3" label="option three" />
323
+ </SingleSelect>
324
+ )
325
+ export const MenuWidthChanging = () => {
326
+ const [toggle, setToggle] = useState(false)
327
+ return (
328
+ <div className="App">
329
+ <Modal dataTest={'myModal'}>
330
+ <ModalTitle>Modal</ModalTitle>
331
+ <ModalContent>
332
+ <div style={{ display: 'flex' }}>
333
+ {toggle && (
334
+ <div
335
+ style={{
336
+ padding: '30px',
337
+ border: '1px solid green',
338
+ }}
339
+ className="toggler"
340
+ >
341
+ Stuff
342
+ </div>
343
+ )}
344
+ <div style={{ flexGrow: 1 }}>
345
+ <SingleSelect
346
+ onChange={({ selected }) =>
347
+ console.log('size changed to ' + selected)
348
+ }
349
+ >
350
+ {options.map(({ name, id }) => (
351
+ <SingleSelectOption
352
+ key={id}
353
+ value={id}
354
+ label={name}
355
+ />
356
+ ))}
357
+ </SingleSelect>
358
+ </div>
359
+ {toggle && (
360
+ <div
361
+ style={{
362
+ padding: '30px',
363
+ border: '1px solid green',
364
+ }}
365
+ className="toggler"
366
+ >
367
+ Stuff
368
+ </div>
369
+ )}
370
+ </div>
371
+ <Button onClick={() => setToggle(!toggle)}>Toggle</Button>
372
+ </ModalContent>
373
+ </Modal>
374
+ </div>
375
+ )
376
+ }
@@ -0,0 +1,146 @@
1
+ import { requiredIf } from '@dhis2/prop-types'
2
+ import { spacers, sharedPropTypes } from '@dhis2/ui-constants'
3
+ import { StatusIcon } from '@dhis2-ui/status-icon'
4
+ import PropTypes from 'prop-types'
5
+ import React from 'react'
6
+ import { Loading as CommonLoading, Select } from '../select/index.js'
7
+ import { FilterableMenu } from './filterable-menu.js'
8
+ import { Input } from './input.js'
9
+ import { Menu } from './menu.js'
10
+
11
+ const SingleSelect = ({
12
+ className,
13
+ selected = '',
14
+ tabIndex,
15
+ maxHeight,
16
+ inputMaxHeight,
17
+ onChange,
18
+ onFocus,
19
+ onKeyDown,
20
+ onBlur,
21
+ loading,
22
+ error,
23
+ warning,
24
+ valid,
25
+ disabled,
26
+ children,
27
+ clearable,
28
+ clearText,
29
+ filterable,
30
+ filterPlaceholder,
31
+ placeholder,
32
+ prefix,
33
+ empty,
34
+ loadingText,
35
+ noMatchText,
36
+ initialFocus,
37
+ dense,
38
+ dataTest = 'dhis2-uicore-singleselect',
39
+ }) => {
40
+ // If the select is filterable, use a filterable menu
41
+ const menu = filterable ? (
42
+ <FilterableMenu
43
+ dataTest={dataTest}
44
+ empty={empty}
45
+ noMatchText={noMatchText}
46
+ placeholder={filterPlaceholder}
47
+ />
48
+ ) : (
49
+ <Menu empty={empty} dataTest={dataTest} />
50
+ )
51
+
52
+ return (
53
+ <div className="root" data-test={dataTest}>
54
+ <div className="root-input">
55
+ <Select
56
+ className={className}
57
+ selected={selected}
58
+ input={
59
+ <Input
60
+ clearable={clearable}
61
+ clearText={clearText}
62
+ dataTest={dataTest}
63
+ placeholder={placeholder}
64
+ prefix={prefix}
65
+ inputMaxHeight={inputMaxHeight}
66
+ />
67
+ }
68
+ menu={menu}
69
+ tabIndex={tabIndex}
70
+ maxHeight={maxHeight}
71
+ onChange={onChange}
72
+ onFocus={onFocus}
73
+ onKeyDown={onKeyDown}
74
+ onBlur={onBlur}
75
+ error={error}
76
+ warning={warning}
77
+ valid={valid}
78
+ disabled={disabled}
79
+ initialFocus={initialFocus}
80
+ dense={dense}
81
+ >
82
+ {children}
83
+ {loading && (
84
+ <CommonLoading
85
+ message={loadingText}
86
+ dataTest={`${dataTest}-loading`}
87
+ />
88
+ )}
89
+ </Select>
90
+ </div>
91
+ <StatusIcon error={error} valid={valid} warning={warning} />
92
+
93
+ <style jsx>{`
94
+ .root {
95
+ align-items: center;
96
+ display: flex;
97
+ gap: ${spacers.dp8};
98
+ }
99
+
100
+ .root-input {
101
+ flex: 1;
102
+ }
103
+ `}</style>
104
+ </div>
105
+ )
106
+ }
107
+
108
+ SingleSelect.propTypes = {
109
+ children: PropTypes.node,
110
+ className: PropTypes.string,
111
+ /** Text on button that clears selection. Required if `clearable` prop is true */
112
+ clearText: requiredIf((props) => props.clearable, PropTypes.string),
113
+ /** Adds a button to clear selection */
114
+ clearable: PropTypes.bool,
115
+ dataTest: PropTypes.string,
116
+ dense: PropTypes.bool,
117
+ disabled: PropTypes.bool,
118
+ /** Text or component to display when there are no options */
119
+ empty: PropTypes.node,
120
+ /** Applies 'error' appearance for validation feedback. Mutually exclusive with `warning` and `valid` props */
121
+ error: sharedPropTypes.statusPropType,
122
+ filterPlaceholder: PropTypes.string,
123
+ /** Adds a filter field to add text to filter options */
124
+ filterable: PropTypes.bool,
125
+ initialFocus: PropTypes.bool,
126
+ inputMaxHeight: PropTypes.string,
127
+ loading: PropTypes.bool,
128
+ loadingText: PropTypes.string,
129
+ maxHeight: PropTypes.string,
130
+ /** Text to show when filter returns no results. Required if `filterable` prop is true */
131
+ noMatchText: requiredIf((props) => props.filterable, PropTypes.string),
132
+ placeholder: PropTypes.string,
133
+ prefix: PropTypes.string,
134
+ selected: PropTypes.string,
135
+ tabIndex: PropTypes.string,
136
+ /** Applies 'valid' appearance for validation feedback. Mutually exclusive with `warning` and `error` props */
137
+ valid: sharedPropTypes.statusPropType,
138
+ /** Applies 'warning' appearance for validation feedback. Mutually exclusive with `valid` and `error` props */
139
+ warning: sharedPropTypes.statusPropType,
140
+ onBlur: PropTypes.func,
141
+ onChange: PropTypes.func,
142
+ onFocus: PropTypes.func,
143
+ onKeyDown: PropTypes.func,
144
+ }
145
+
146
+ export { SingleSelect }