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