@dhis2-ui/select 10.16.2 → 10.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/package.json +17 -16
  2. package/src/index.js +9 -0
  3. package/src/locales/ar/translations.json +7 -0
  4. package/src/locales/ar_IQ/translations.json +7 -0
  5. package/src/locales/ckb/translations.json +7 -0
  6. package/src/locales/cs/translations.json +7 -0
  7. package/src/locales/da/translations.json +7 -0
  8. package/src/locales/en/translations.json +11 -0
  9. package/src/locales/en_US/translations.json +9 -0
  10. package/src/locales/es/translations.json +7 -0
  11. package/src/locales/es_419/translations.json +7 -0
  12. package/src/locales/fr/translations.json +7 -0
  13. package/src/locales/hi_IN/translations.json +7 -0
  14. package/src/locales/id/translations.json +7 -0
  15. package/src/locales/index.js +80 -0
  16. package/src/locales/km/translations.json +7 -0
  17. package/src/locales/lo/translations.json +7 -0
  18. package/src/locales/my/translations.json +7 -0
  19. package/src/locales/nb/translations.json +7 -0
  20. package/src/locales/nl/translations.json +7 -0
  21. package/src/locales/prs/translations.json +7 -0
  22. package/src/locales/ps/translations.json +7 -0
  23. package/src/locales/pt/translations.json +7 -0
  24. package/src/locales/pt_BR/translations.json +7 -0
  25. package/src/locales/ru/translations.json +7 -0
  26. package/src/locales/sv/translations.json +7 -0
  27. package/src/locales/tet/translations.json +7 -0
  28. package/src/locales/tg/translations.json +7 -0
  29. package/src/locales/uk/translations.json +7 -0
  30. package/src/locales/ur/translations.json +7 -0
  31. package/src/locales/uz_Latn/translations.json +7 -0
  32. package/src/locales/uz_UZ_Cyrl/translations.json +7 -0
  33. package/src/locales/uz_UZ_Latn/translations.json +7 -0
  34. package/src/locales/vi/translations.json +7 -0
  35. package/src/locales/zh/translations.json +9 -0
  36. package/src/locales/zh_CN/translations.json +7 -0
  37. package/src/multi-select/__tests__/multi-select.test.js +23 -0
  38. package/src/multi-select/__tests__/selection-list.test.js +102 -0
  39. package/src/multi-select/features/accepts_blur_cb/index.js +14 -0
  40. package/src/multi-select/features/accepts_blur_cb.feature +8 -0
  41. package/src/multi-select/features/accepts_focus_cb/index.js +14 -0
  42. package/src/multi-select/features/accepts_focus_cb.feature +6 -0
  43. package/src/multi-select/features/accepts_initial_focus/index.js +5 -0
  44. package/src/multi-select/features/accepts_initial_focus.feature +5 -0
  45. package/src/multi-select/features/accepts_loading/index.js +22 -0
  46. package/src/multi-select/features/accepts_loading.feature +14 -0
  47. package/src/multi-select/features/accepts_max_height/index.js +31 -0
  48. package/src/multi-select/features/accepts_max_height.feature +11 -0
  49. package/src/multi-select/features/accepts_placeholder/index.js +25 -0
  50. package/src/multi-select/features/accepts_placeholder.feature +10 -0
  51. package/src/multi-select/features/accepts_prefix/index.js +19 -0
  52. package/src/multi-select/features/accepts_prefix.feature +10 -0
  53. package/src/multi-select/features/allows_invalid_options/index.js +27 -0
  54. package/src/multi-select/features/allows_invalid_options.feature +12 -0
  55. package/src/multi-select/features/allows_selecting/index.js +81 -0
  56. package/src/multi-select/features/allows_selecting.feature +44 -0
  57. package/src/multi-select/features/can_be_cleared/index.js +22 -0
  58. package/src/multi-select/features/can_be_cleared.feature +6 -0
  59. package/src/multi-select/features/can_be_disabled/index.js +36 -0
  60. package/src/multi-select/features/can_be_disabled.feature +32 -0
  61. package/src/multi-select/features/can_be_empty/index.js +28 -0
  62. package/src/multi-select/features/can_be_empty.feature +16 -0
  63. package/src/multi-select/features/can_be_filtered/index.js +23 -0
  64. package/src/multi-select/features/can_be_filtered.feature +14 -0
  65. package/src/multi-select/features/can_be_opened_and_closed/index.js +32 -0
  66. package/src/multi-select/features/can_be_opened_and_closed.feature +41 -0
  67. package/src/multi-select/features/common/index.js +49 -0
  68. package/src/multi-select/features/duplicate_option_values/index.js +21 -0
  69. package/src/multi-select/features/duplicate_option_values.feature +7 -0
  70. package/src/multi-select/features/menu_width_matches_input/index.js +53 -0
  71. package/src/multi-select/features/menu_width_matches_input.feature +11 -0
  72. package/src/multi-select/features/position/index.js +160 -0
  73. package/src/multi-select/features/position.feature +46 -0
  74. package/src/multi-select/features/shows_selections/index.js +18 -0
  75. package/src/multi-select/features/shows_selections.feature +9 -0
  76. package/src/multi-select/filterable-menu.js +43 -0
  77. package/src/multi-select/index.js +1 -0
  78. package/src/multi-select/input.js +129 -0
  79. package/src/multi-select/menu.js +91 -0
  80. package/src/multi-select/multi-select.e2e.stories.js +407 -0
  81. package/src/multi-select/multi-select.js +148 -0
  82. package/src/multi-select/multi-select.prod.stories.js +408 -0
  83. package/src/multi-select/selection-list.js +79 -0
  84. package/src/multi-select-field/__tests__/multi-select-field.test.js +29 -0
  85. package/src/multi-select-field/features/accepts_help_text/index.js +9 -0
  86. package/src/multi-select-field/features/accepts_help_text.feature +5 -0
  87. package/src/multi-select-field/features/accepts_label/index.js +9 -0
  88. package/src/multi-select-field/features/accepts_label.feature +5 -0
  89. package/src/multi-select-field/features/accepts_validation_text/index.js +9 -0
  90. package/src/multi-select-field/features/accepts_validation_text.feature +5 -0
  91. package/src/multi-select-field/features/can_be_required/index.js +11 -0
  92. package/src/multi-select-field/features/can_be_required.feature +5 -0
  93. package/src/multi-select-field/features/has_default_clear_text/index.js +16 -0
  94. package/src/multi-select-field/features/has_default_clear_text.feature +6 -0
  95. package/src/multi-select-field/features/has_default_empty_text/index.js +13 -0
  96. package/src/multi-select-field/features/has_default_empty_text.feature +6 -0
  97. package/src/multi-select-field/features/has_default_filter_nomatch_text/index.js +17 -0
  98. package/src/multi-select-field/features/has_default_filter_nomatch_text.feature +7 -0
  99. package/src/multi-select-field/features/has_default_filter_placeholder/index.js +15 -0
  100. package/src/multi-select-field/features/has_default_filter_placeholder.feature +6 -0
  101. package/src/multi-select-field/features/has_default_loading_text/index.js +13 -0
  102. package/src/multi-select-field/features/has_default_loading_text.feature +6 -0
  103. package/src/multi-select-field/index.js +1 -0
  104. package/src/multi-select-field/multi-select-field.e2e.stories.js +45 -0
  105. package/src/multi-select-field/multi-select-field.js +178 -0
  106. package/src/multi-select-field/multi-select-field.prod.stories.js +137 -0
  107. package/src/multi-select-option/index.js +1 -0
  108. package/src/multi-select-option/multi-select-option.js +70 -0
  109. package/src/select/__tests__/select.test.js +31 -0
  110. package/src/select/debounce/debounce.js +30 -0
  111. package/src/select/debounce/debounce.spec.js +35 -0
  112. package/src/select/empty.js +28 -0
  113. package/src/select/filter-input.js +42 -0
  114. package/src/select/filterable-menu.js +106 -0
  115. package/src/select/index.js +14 -0
  116. package/src/select/input-clear-button.js +66 -0
  117. package/src/select/input-placeholder.js +30 -0
  118. package/src/select/input-prefix.js +32 -0
  119. package/src/select/input-wrapper.js +114 -0
  120. package/src/select/loading.js +33 -0
  121. package/src/select/menu-wrapper.js +52 -0
  122. package/src/select/no-match.js +28 -0
  123. package/src/select/option-helpers.js +34 -0
  124. package/src/select/select.js +256 -0
  125. package/src/simple-single-select/__stories__/DefaultPosition.js +37 -0
  126. package/src/simple-single-select/__stories__/Dense.js +17 -0
  127. package/src/simple-single-select/__stories__/Empty.js +15 -0
  128. package/src/simple-single-select/__stories__/EmptyWithEmptyNode.js +27 -0
  129. package/src/simple-single-select/__stories__/EmptyWithEmptyText.js +16 -0
  130. package/src/simple-single-select/__stories__/FlippedPosition.js +36 -0
  131. package/src/simple-single-select/__stories__/InfiniteLoading.js +65 -0
  132. package/src/simple-single-select/__stories__/Loading.js +15 -0
  133. package/src/simple-single-select/__stories__/ShiftedIntoView.js +35 -0
  134. package/src/simple-single-select/__stories__/WithClearButton.js +26 -0
  135. package/src/simple-single-select/__stories__/WithCustomLowMaxHeight.js +17 -0
  136. package/src/simple-single-select/__stories__/WithCustomOptions.js +78 -0
  137. package/src/simple-single-select/__stories__/WithDisabledOption.js +25 -0
  138. package/src/simple-single-select/__stories__/WithFilterField.js +30 -0
  139. package/src/simple-single-select/__stories__/WithInitialFocus.js +29 -0
  140. package/src/simple-single-select/__stories__/WithManyOptions.js +19 -0
  141. package/src/simple-single-select/__stories__/WithMultipleSelectsAndScrollableParent.js +65 -0
  142. package/src/simple-single-select/__stories__/WithNoMatchText.js +25 -0
  143. package/src/simple-single-select/__stories__/WithOnBlur.js +22 -0
  144. package/src/simple-single-select/__stories__/WithOnFocus.js +22 -0
  145. package/src/simple-single-select/__stories__/WithOptionsAndDisabled.js +17 -0
  146. package/src/simple-single-select/__stories__/WithOptionsAndLoading.js +23 -0
  147. package/src/simple-single-select/__stories__/WithOptionsAndLoadingText.js +24 -0
  148. package/src/simple-single-select/__stories__/WithPlaceholder.js +18 -0
  149. package/src/simple-single-select/__stories__/WithPlaceholderAndSelection.js +20 -0
  150. package/src/simple-single-select/__stories__/WithPrefix.js +17 -0
  151. package/src/simple-single-select/__stories__/WithPrefixAndSelection.js +20 -0
  152. package/src/simple-single-select/__stories__/WithRTL.js +29 -0
  153. package/src/simple-single-select/__stories__/WithScrollingParent.js +51 -0
  154. package/src/simple-single-select/__stories__/WithSelection.js +19 -0
  155. package/src/simple-single-select/__stories__/WithSelectionAndDisabled.js +20 -0
  156. package/src/simple-single-select/__stories__/WithServerSideFilteringDemo.js +245 -0
  157. package/src/simple-single-select/__stories__/WithSimpleFilteringDemo.js +50 -0
  158. package/src/simple-single-select/__stories__/WithoutOptionsAndLoading.js +16 -0
  159. package/src/simple-single-select/__stories__/WithoutSelection.js +17 -0
  160. package/src/simple-single-select/__stories__/options.js +96 -0
  161. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/CustomDataProvider.js +70 -0
  162. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/dataElements.js +4271 -0
  163. package/src/simple-single-select/__stories__/simple-single-select-server-side-filtering/index.js +1 -0
  164. package/src/simple-single-select/features/keyboard-interactions.test.e2e.js +581 -0
  165. package/src/simple-single-select/features/menu-positioning.test.e2e.js +187 -0
  166. package/src/simple-single-select/index.js +2 -0
  167. package/src/simple-single-select/is-option-hidden.js +21 -0
  168. package/src/simple-single-select/menu/empty.js +27 -0
  169. package/src/simple-single-select/menu/filter.js +61 -0
  170. package/src/simple-single-select/menu/index.js +1 -0
  171. package/src/simple-single-select/menu/loading.js +38 -0
  172. package/src/simple-single-select/menu/menu.js +171 -0
  173. package/src/simple-single-select/menu/no-match.js +28 -0
  174. package/src/simple-single-select/menu/option.js +161 -0
  175. package/src/simple-single-select/menu/options-list.js +107 -0
  176. package/src/simple-single-select/selected-value/clear-button.js +71 -0
  177. package/src/simple-single-select/selected-value/container.js +146 -0
  178. package/src/simple-single-select/selected-value/index.js +1 -0
  179. package/src/simple-single-select/selected-value/placeholder.js +24 -0
  180. package/src/simple-single-select/selected-value/prefix.js +26 -0
  181. package/src/simple-single-select/selected-value/selected-value.js +174 -0
  182. package/src/simple-single-select/shared-prop-types.js +8 -0
  183. package/src/simple-single-select/simple-single-select.e2e.stories.js +216 -0
  184. package/src/simple-single-select/simple-single-select.js +389 -0
  185. package/src/simple-single-select/simple-single-select.prod.stories.js +41 -0
  186. package/src/simple-single-select/simple-single-select.test.js +1136 -0
  187. package/src/simple-single-select/use-handle-key-press/index.js +2 -0
  188. package/src/simple-single-select/use-handle-key-press/use-focus-option.js +24 -0
  189. package/src/simple-single-select/use-handle-key-press/use-handle-key-press-on-combobox.js +259 -0
  190. package/src/simple-single-select/use-handle-key-press/use-handle-key-press-on-filter-input.js +147 -0
  191. package/src/simple-single-select/use-handle-key-press/use-handle-typing.js +79 -0
  192. package/src/simple-single-select/use-handle-key-press/use-highlight-first-option-on-previous-page.js +62 -0
  193. package/src/simple-single-select/use-handle-key-press/use-highlight-first-visible-option.js +38 -0
  194. package/src/simple-single-select/use-handle-key-press/use-highlight-last-option-on-next-page.js +60 -0
  195. package/src/simple-single-select/use-handle-key-press/use-highlight-last-visible-option.js +52 -0
  196. package/src/simple-single-select/use-handle-key-press/use-page-down.js +50 -0
  197. package/src/simple-single-select/use-handle-key-press/use-page-up.js +50 -0
  198. package/src/simple-single-select/use-handle-key-press/utils.js +23 -0
  199. package/src/simple-single-select-field/index.js +1 -0
  200. package/src/simple-single-select-field/simple-single-select-field.js +198 -0
  201. package/src/simple-single-select-field/simple-single-select-field.prod.stories.js +188 -0
  202. package/src/simple-single-select-field/simple-single-select-field.test.js +144 -0
  203. package/src/single-select/__tests__/selection.test.js +102 -0
  204. package/src/single-select/__tests__/single-select.test.js +23 -0
  205. package/src/single-select/features/accepts_blur_cb/index.js +14 -0
  206. package/src/single-select/features/accepts_blur_cb.feature +8 -0
  207. package/src/single-select/features/accepts_focus_cb/index.js +14 -0
  208. package/src/single-select/features/accepts_focus_cb.feature +6 -0
  209. package/src/single-select/features/accepts_initial_focus/index.js +5 -0
  210. package/src/single-select/features/accepts_initial_focus.feature +5 -0
  211. package/src/single-select/features/accepts_loading/index.js +22 -0
  212. package/src/single-select/features/accepts_loading.feature +14 -0
  213. package/src/single-select/features/accepts_max_height/index.js +31 -0
  214. package/src/single-select/features/accepts_max_height.feature +11 -0
  215. package/src/single-select/features/accepts_placeholder/index.js +25 -0
  216. package/src/single-select/features/accepts_placeholder.feature +10 -0
  217. package/src/single-select/features/accepts_prefix/index.js +19 -0
  218. package/src/single-select/features/accepts_prefix.feature +10 -0
  219. package/src/single-select/features/allows_invalid_options/index.js +28 -0
  220. package/src/single-select/features/allows_invalid_options.feature +12 -0
  221. package/src/single-select/features/allows_selecting/index.js +38 -0
  222. package/src/single-select/features/allows_selecting.feature +21 -0
  223. package/src/single-select/features/can_be_cleared/index.js +22 -0
  224. package/src/single-select/features/can_be_cleared.feature +6 -0
  225. package/src/single-select/features/can_be_disabled/index.js +39 -0
  226. package/src/single-select/features/can_be_disabled.feature +32 -0
  227. package/src/single-select/features/can_be_empty/index.js +28 -0
  228. package/src/single-select/features/can_be_empty.feature +16 -0
  229. package/src/single-select/features/can_be_filtered/index.js +23 -0
  230. package/src/single-select/features/can_be_filtered.feature +14 -0
  231. package/src/single-select/features/can_be_opened_and_closed/index.js +32 -0
  232. package/src/single-select/features/can_be_opened_and_closed.feature +41 -0
  233. package/src/single-select/features/common/index.js +44 -0
  234. package/src/single-select/features/duplicate_option_values/index.js +26 -0
  235. package/src/single-select/features/duplicate_option_values.feature +7 -0
  236. package/src/single-select/features/menu_width_matches_input/index.js +53 -0
  237. package/src/single-select/features/menu_width_matches_input.feature +11 -0
  238. package/src/single-select/features/position/index.js +111 -0
  239. package/src/single-select/features/position.feature +26 -0
  240. package/src/single-select/features/shows_selections/index.js +9 -0
  241. package/src/single-select/features/shows_selections.feature +5 -0
  242. package/src/single-select/filterable-menu.js +41 -0
  243. package/src/single-select/index.js +1 -0
  244. package/src/single-select/input.js +101 -0
  245. package/src/single-select/menu.js +75 -0
  246. package/src/single-select/selection.js +60 -0
  247. package/src/single-select/single-select.e2e.stories.js +376 -0
  248. package/src/single-select/single-select.js +146 -0
  249. package/src/single-select/single-select.prod.stories.js +358 -0
  250. package/src/single-select-field/__tests__/single-select-field.test.js +29 -0
  251. package/src/single-select-field/features/accepts_help_text/index.js +9 -0
  252. package/src/single-select-field/features/accepts_help_text.feature +5 -0
  253. package/src/single-select-field/features/accepts_label/index.js +9 -0
  254. package/src/single-select-field/features/accepts_label.feature +5 -0
  255. package/src/single-select-field/features/accepts_validation_text/index.js +9 -0
  256. package/src/single-select-field/features/accepts_validation_text.feature +5 -0
  257. package/src/single-select-field/features/can_be_required/index.js +11 -0
  258. package/src/single-select-field/features/can_be_required.feature +5 -0
  259. package/src/single-select-field/features/has_default_clear_text/index.js +16 -0
  260. package/src/single-select-field/features/has_default_clear_text.feature +6 -0
  261. package/src/single-select-field/features/has_default_empty_text/index.js +13 -0
  262. package/src/single-select-field/features/has_default_empty_text.feature +6 -0
  263. package/src/single-select-field/features/has_default_filter_nomatch_text/index.js +17 -0
  264. package/src/single-select-field/features/has_default_filter_nomatch_text.feature +7 -0
  265. package/src/single-select-field/features/has_default_filter_placeholder/index.js +15 -0
  266. package/src/single-select-field/features/has_default_filter_placeholder.feature +6 -0
  267. package/src/single-select-field/features/has_default_loading_text/index.js +13 -0
  268. package/src/single-select-field/features/has_default_loading_text.feature +6 -0
  269. package/src/single-select-field/index.js +1 -0
  270. package/src/single-select-field/single-select-field.e2e.stories.js +45 -0
  271. package/src/single-select-field/single-select-field.js +178 -0
  272. package/src/single-select-field/single-select-field.prod.stories.js +146 -0
  273. package/src/single-select-option/index.js +1 -0
  274. package/src/single-select-option/single-select-option.js +84 -0
@@ -0,0 +1,32 @@
1
+ Feature: Disabled SingleSelect
2
+
3
+ Scenario: The disabled singleselect has a selection
4
+ Given a disabled SingleSelect with options and a selection is rendered
5
+ Then the selection is displayed
6
+
7
+ Scenario: The user clicks a disabled SingleSelect
8
+ Given a disabled SingleSelect with options is rendered
9
+ And the SingleSelect is closed
10
+ When the SingleSelect input is clicked
11
+ Then the options are not rendered
12
+
13
+ Scenario: The user presses the down arrowkey
14
+ Given a disabled SingleSelect with options is rendered
15
+ And the SingleSelect is closed
16
+ And the SingleSelect is focused
17
+ When the down arrowkey is pressed on the focused element
18
+ Then the options are not rendered
19
+
20
+ Scenario: The user presses the up arrowkey
21
+ Given a disabled SingleSelect with options is rendered
22
+ And the SingleSelect is closed
23
+ And the SingleSelect is focused
24
+ When the up arrowkey is pressed on the focused element
25
+ Then the options are not rendered
26
+
27
+ Scenario: The user presses the spacebar
28
+ Given a disabled SingleSelect with options is rendered
29
+ And the SingleSelect is closed
30
+ And the SingleSelect is focused
31
+ When the spacebar is pressed on the focused element
32
+ Then the options are not rendered
@@ -0,0 +1,28 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('an empty SingleSelect is rendered', () => {
4
+ cy.visitStory('SingleSelect', 'Empty')
5
+ })
6
+
7
+ Given('an empty SingleSelect with custom empty text is rendered', () => {
8
+ cy.visitStory('SingleSelect', 'Empty with empty text')
9
+ })
10
+
11
+ Given('an empty SingleSelect with custom empty component is rendered', () => {
12
+ cy.visitStory('SingleSelect', 'Empty with empty component')
13
+ })
14
+
15
+ Then('an empty menu is displayed', () => {
16
+ cy.get('[data-test="dhis2-uicore-layer"]').should('exist')
17
+ })
18
+
19
+ Then('the custom empty text is displayed', () => {
20
+ cy.get('[data-test="dhis2-uicore-singleselect-empty"]')
21
+ .contains('Custom empty text')
22
+ .should('be.visible')
23
+ })
24
+
25
+ Then('the custom empty component is displayed', () => {
26
+ cy.contains('Custom empty component').should('be.visible')
27
+ cy.get('.custom-empty').should('exist')
28
+ })
@@ -0,0 +1,16 @@
1
+ Feature: Loading status
2
+
3
+ Scenario: The user opens an empty SingleSelect
4
+ Given an empty SingleSelect is rendered
5
+ When the SingleSelect input is clicked
6
+ Then an empty menu is displayed
7
+
8
+ Scenario: The user opens an empty SingleSelect with custom empty text
9
+ Given an empty SingleSelect with custom empty text is rendered
10
+ When the SingleSelect input is clicked
11
+ Then the custom empty text is displayed
12
+
13
+ Scenario: The user opens an empty SingleSelect with a custom empty component
14
+ Given an empty SingleSelect with custom empty component is rendered
15
+ When the SingleSelect input is clicked
16
+ Then the custom empty component is displayed
@@ -0,0 +1,23 @@
1
+ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a filterable SingleSelect with options is rendered', () => {
4
+ cy.visitStory('SingleSelect', 'With filter field')
5
+ })
6
+
7
+ When('the user enters a filter string', () => {
8
+ cy.focused().type('one')
9
+ })
10
+
11
+ When("the user enters a filter string that doesn't match any options", () => {
12
+ cy.focused().type('does not exist')
13
+ })
14
+
15
+ Then('the matching options are displayed', () => {
16
+ cy.contains('option one').should('be.visible')
17
+ cy.contains('option two').should('not.exist')
18
+ cy.contains('option three').should('not.exist')
19
+ })
20
+
21
+ Then('the no match text is displayed', () => {
22
+ cy.contains('No match found').should('be.visible')
23
+ })
@@ -0,0 +1,14 @@
1
+ Feature: Filtering the SingleSelect options
2
+
3
+ Scenario: The user enters a filter string to filter the options
4
+ Given a filterable SingleSelect with options is rendered
5
+ And the SingleSelect is open
6
+ When the user enters a filter string
7
+ Then the matching options are displayed
8
+
9
+ Scenario: The user enters a filter string that doesn't match any options
10
+ Given a filterable SingleSelect with options is rendered
11
+ And the SingleSelect is open
12
+ When the user enters a filter string that doesn't match any options
13
+ Then the no match text is displayed
14
+ And the options are not rendered
@@ -0,0 +1,32 @@
1
+ import { Given, When } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a SingleSelect with options is rendered', () => {
4
+ cy.visitStory('SingleSelect', 'With options')
5
+ })
6
+
7
+ Given('the SingleSelect is closed', () => {
8
+ cy.contains('option one').should('not.exist')
9
+ cy.contains('option two').should('not.exist')
10
+ cy.contains('option three').should('not.exist')
11
+ })
12
+
13
+ Given('the SingleSelect is focused', () => {
14
+ cy.get('[data-test="dhis2-uicore-select-input"]').focus()
15
+ cy.focused().should('have.attr', 'data-test', 'dhis2-uicore-select-input')
16
+ })
17
+
18
+ When('the down arrowkey is pressed on the focused element', () => {
19
+ cy.focused().type('{downarrow}')
20
+ })
21
+
22
+ When('the spacebar is pressed on the focused element', () => {
23
+ cy.focused().type(' ')
24
+ })
25
+
26
+ When('the up arrowkey is pressed on the focused element', () => {
27
+ cy.focused().type('{uparrow}')
28
+ })
29
+
30
+ When('the escape key is pressed on the focused element', () => {
31
+ cy.focused().type('{esc}')
32
+ })
@@ -0,0 +1,41 @@
1
+ Feature: Opening and closing the SingleSelect
2
+
3
+ Scenario: The user clicks the SingleSelect input to display the options
4
+ Given a SingleSelect with options is rendered
5
+ And the SingleSelect is closed
6
+ When the SingleSelect input is clicked
7
+ Then the options are displayed
8
+
9
+ Scenario: The user presses the down arrowkey to display the options
10
+ Given a SingleSelect with options is rendered
11
+ And the SingleSelect is closed
12
+ And the SingleSelect is focused
13
+ When the down arrowkey is pressed on the focused element
14
+ Then the options are displayed
15
+
16
+ Scenario: The user presses the up arrowkey to display the options
17
+ Given a SingleSelect with options is rendered
18
+ And the SingleSelect is closed
19
+ And the SingleSelect is focused
20
+ When the up arrowkey is pressed on the focused element
21
+ Then the options are displayed
22
+
23
+ Scenario: The user presses the spacebar to display the options
24
+ Given a SingleSelect with options is rendered
25
+ And the SingleSelect is closed
26
+ And the SingleSelect is focused
27
+ When the spacebar is pressed on the focused element
28
+ Then the options are displayed
29
+
30
+ Scenario: The user clicks the backdrop layer to hide the options
31
+ Given a SingleSelect with options is rendered
32
+ And the SingleSelect is open
33
+ When the user clicks the backdrop layer
34
+ Then the options are not rendered
35
+
36
+ Scenario: The user presses the escape key to hide the options
37
+ Given a SingleSelect with options is rendered
38
+ And the SingleSelect is open
39
+ And the SingleSelect is focused
40
+ When the escape key is pressed on the focused element
41
+ Then the options are not rendered
@@ -0,0 +1,44 @@
1
+ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a SingleSelect with options and onChange handler is rendered', () => {
4
+ cy.visitStory('SingleSelect', 'With options and on change')
5
+ })
6
+
7
+ Given(
8
+ 'a SingleSelect with options, a selection and onChange handler is rendered',
9
+ () => {
10
+ cy.visitStory('SingleSelect', 'With options, a selection and on change')
11
+ }
12
+ )
13
+
14
+ Given('the SingleSelect is open', () => {
15
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
16
+
17
+ cy.contains('option one').should('exist')
18
+ cy.contains('option two').should('exist')
19
+ cy.contains('option three').should('exist')
20
+ })
21
+
22
+ When('the SingleSelect input is clicked', () => {
23
+ cy.get('[data-test="dhis2-uicore-select-input"]').click()
24
+ })
25
+
26
+ When('the user clicks the backdrop layer', () => {
27
+ cy.get('[data-test="dhis2-uicore-layer"]').click()
28
+ })
29
+
30
+ Then('the options are not rendered', () => {
31
+ cy.contains('option one').should('not.exist')
32
+ cy.contains('option two').should('not.exist')
33
+ cy.contains('option three').should('not.exist')
34
+ })
35
+
36
+ Then('the options are displayed', () => {
37
+ cy.contains('option one').should('be.visible')
38
+ cy.contains('option two').should('be.visible')
39
+ cy.contains('option three').should('be.visible')
40
+ })
41
+
42
+ Then('the SingleSelect has focus', () => {
43
+ cy.focused().should('have.attr', 'data-test', 'dhis2-uicore-select-input')
44
+ })
@@ -0,0 +1,26 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given(
4
+ 'a SingleSelect with options with a duplicate value and this value is selected',
5
+ () => {
6
+ cy.visitStory('SingleSelect', 'With duplicate selected option values')
7
+ }
8
+ )
9
+ Then(
10
+ 'the first option with the selected value is displayed in the input',
11
+ () => {
12
+ cy.get('[data-test="dhis2-uicore-select-input"]').should(
13
+ 'contain',
14
+ 'option one'
15
+ )
16
+ }
17
+ )
18
+ Then('both options are highlighted in the dropdown', () => {
19
+ cy.get(
20
+ '[data-test="dhis2-uicore-singleselectoption"]:contains("option one")'
21
+ ).should('have.class', 'active')
22
+
23
+ cy.get(
24
+ '[data-test="dhis2-uicore-singleselectoption"]:contains("option one a")'
25
+ ).should('have.class', 'active')
26
+ })
@@ -0,0 +1,7 @@
1
+ Feature: Duplicate option values result in a conflict between the input and dropdown UI
2
+
3
+ Scenario: The SingleSelect has options with a duplicate value and this value is selected
4
+ Given a SingleSelect with options with a duplicate value and this value is selected
5
+ And the SingleSelect is open
6
+ Then the first option with the selected value is displayed in the input
7
+ But both options are highlighted in the dropdown
@@ -0,0 +1,53 @@
1
+ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a SingleSelect with hidden sibling elements', () => {
4
+ cy.visitStory('SingleSelect', 'Menu width changing')
5
+ cy.get('[data-test="dhis2-uicore-singleselect"]').then(($el) => {
6
+ cy.wrap($el.outerWidth()).as('originalWidth')
7
+ })
8
+ })
9
+ When('the button is clicked', () => {
10
+ cy.get('button:contains("Toggle")').click()
11
+ })
12
+ Then('the siblings are displayed', () => {
13
+ cy.get('.toggler').should('exist').and('have.length', 2)
14
+ })
15
+ Then('the menu width has decreased', () => {
16
+ cy.get('[data-test="dhis2-uicore-singleselect"]').then(($el) => {
17
+ const newWidth = $el.outerWidth()
18
+ cy.get('@originalWidth').should('be.greaterThan', newWidth)
19
+ })
20
+ })
21
+ When('the SingleSelect menu is open', () => {
22
+ cy.get('[data-test="dhis2-uicore-select-menu-menuwrapper"]').should(
23
+ 'be.visible'
24
+ )
25
+ })
26
+ Then('the SingleSelect input is left aligned with the menu', () => {
27
+ const selectDataTest = '[data-test="dhis2-uicore-singleselect"]'
28
+ const menuDataTest = '[data-test="dhis2-uicore-select-menu-menuwrapper"]'
29
+
30
+ cy.getAll(selectDataTest, menuDataTest).should(([inputs, menus]) => {
31
+ expect(inputs.length).to.equal(1)
32
+ expect(menus.length).to.equal(1)
33
+
34
+ const $input = inputs[0]
35
+ const $menu = menus[0]
36
+
37
+ const inputRect = $input.getBoundingClientRect()
38
+ const menuRect = $menu.getBoundingClientRect()
39
+
40
+ expect(Math.round(inputRect.left)).to.equal(Math.round(menuRect.left))
41
+ })
42
+ })
43
+ When('the SingleSelect input and menu have the same width', () => {
44
+ const inputSelector = '[data-test="dhis2-uicore-select"]'
45
+ const menuSelector = '[data-test="dhis2-uicore-select-menu-menuwrapper"]'
46
+
47
+ cy.getAll(inputSelector, menuSelector).should(([$input, $menu]) => {
48
+ const inputWidth = Math.round($input.outerWidth())
49
+ const menuWidth = Math.round($menu.outerWidth())
50
+
51
+ expect(inputWidth).to.equal(menuWidth)
52
+ })
53
+ })
@@ -0,0 +1,11 @@
1
+ Feature: The menu always has the same size as the input
2
+
3
+ Scenario: The SingleSelect input changes width while the menu is closed
4
+ Given a SingleSelect with hidden sibling elements
5
+ When the button is clicked
6
+ Then the siblings are displayed
7
+ And the menu width has decreased
8
+ When the SingleSelect input is clicked
9
+ Then the SingleSelect menu is open
10
+ And the SingleSelect input is left aligned with the menu
11
+ And the SingleSelect input and menu have the same width
@@ -0,0 +1,111 @@
1
+ import { Given, Then, When } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given(
4
+ 'there is enough space below the anchor to fit the SingleSelect menu',
5
+ () => {
6
+ cy.visitStory('SingleSelect', 'Default position')
7
+ }
8
+ )
9
+
10
+ Given(
11
+ 'there is not enough space below the anchor to fit the SingleSelect menu',
12
+ () => {
13
+ cy.visitStory('SingleSelect', 'Flipped position')
14
+ }
15
+ )
16
+
17
+ Given(
18
+ 'there is not enough space above or below the anchor to fit the SingleSelect menu',
19
+ () => {
20
+ cy.visitStory('SingleSelect', 'Shifted into view')
21
+ }
22
+ )
23
+
24
+ When('the SingleSelect is clicked', () => {
25
+ cy.get('[data-test="dhis2-uicore-singleselect"]').click()
26
+ })
27
+
28
+ When('the window is scrolled down', () => {
29
+ // Ensure the body can scroll first
30
+ cy.get('body').then(($body) => {
31
+ $body.height('5000px')
32
+ })
33
+ cy.scrollTo(0, 800)
34
+ })
35
+
36
+ Then('the top of the menu is aligned with the bottom of the input', () => {
37
+ const selectDataTest = '[data-test="dhis2-uicore-singleselect"]'
38
+ const menuDataTest = '[data-test="dhis2-uicore-select-menu-menuwrapper"]'
39
+
40
+ cy.getAll(selectDataTest, menuDataTest).should(([selects, menus]) => {
41
+ expect(selects.length).to.equal(1)
42
+ expect(menus.length).to.equal(1)
43
+
44
+ const $select = selects[0]
45
+ const $menu = menus[0]
46
+
47
+ const selectRect = $select.getBoundingClientRect()
48
+ const menuRect = $menu.getBoundingClientRect()
49
+
50
+ expect(menuRect.top).to.be.closeTo(selectRect.bottom, 1)
51
+ })
52
+ })
53
+
54
+ Then('the bottom of the menu is aligned with the top of the input', () => {
55
+ const selectDataTest = '[data-test="dhis2-uicore-singleselect"]'
56
+ const menuDataTest = '[data-test="dhis2-uicore-select-menu-menuwrapper"]'
57
+
58
+ cy.getAll(selectDataTest, menuDataTest).should(([selects, menus]) => {
59
+ expect(selects.length).to.equal(1)
60
+ expect(menus.length).to.equal(1)
61
+
62
+ const $select = selects[0]
63
+ const $menu = menus[0]
64
+
65
+ const selectRect = $select.getBoundingClientRect()
66
+ const menuRect = $menu.getBoundingClientRect()
67
+
68
+ expect(selectRect.top).to.be.closeTo(menuRect.bottom, 1)
69
+ })
70
+ })
71
+
72
+ Then('it is rendered on top of the SingleSelect', () => {
73
+ const selectDataTest = '[data-test="dhis2-uicore-singleselect"]'
74
+ const menuDataTest = '[data-test="dhis2-uicore-select-menu-menuwrapper"]'
75
+
76
+ cy.getAll(selectDataTest, menuDataTest).should(([selects, menus]) => {
77
+ expect(selects.length).to.equal(1)
78
+ expect(menus.length).to.equal(1)
79
+
80
+ const $select = selects[0]
81
+ const $menu = menus[0]
82
+
83
+ const selectRect = $select.getBoundingClientRect()
84
+ const menuRect = $menu.getBoundingClientRect()
85
+
86
+ expect(selectRect.top).to.be.greaterThan(menuRect.top)
87
+ expect(menuRect.bottom).to.be.greaterThan(selectRect.bottom)
88
+ })
89
+ })
90
+
91
+ Then(
92
+ 'the left of the SingleSelect is aligned with the left of the anchor',
93
+ () => {
94
+ const selectDataTest = '[data-test="dhis2-uicore-singleselect"]'
95
+ const menuDataTest =
96
+ '[data-test="dhis2-uicore-select-menu-menuwrapper"]'
97
+
98
+ cy.getAll(selectDataTest, menuDataTest).should(([selects, menus]) => {
99
+ expect(selects.length).to.equal(1)
100
+ expect(menus.length).to.equal(1)
101
+
102
+ const $select = selects[0]
103
+ const $menu = menus[0]
104
+
105
+ const selectRect = $select.getBoundingClientRect()
106
+ const menuRect = $menu.getBoundingClientRect()
107
+
108
+ expect(selectRect.left).to.be.closeTo(menuRect.left, 1)
109
+ })
110
+ }
111
+ )
@@ -0,0 +1,26 @@
1
+ Feature: Position of SingleSelect menu dropdown
2
+
3
+ Scenario: Default rendering
4
+ Given there is enough space below the anchor to fit the SingleSelect menu
5
+ When the SingleSelect is clicked
6
+ Then the top of the menu is aligned with the bottom of the input
7
+ And the left of the SingleSelect is aligned with the left of the anchor
8
+
9
+ Scenario: Flipped rendering when insufficient space below
10
+ Given there is not enough space below the anchor to fit the SingleSelect menu
11
+ When the SingleSelect is clicked
12
+ Then the bottom of the menu is aligned with the top of the input
13
+ And the left of the SingleSelect is aligned with the left of the anchor
14
+
15
+ Scenario: Shifting into view when insufficient space below and above
16
+ Given there is not enough space above or below the anchor to fit the SingleSelect menu
17
+ When the SingleSelect is clicked
18
+ Then it is rendered on top of the SingleSelect
19
+ And the left of the SingleSelect is aligned with the left of the anchor
20
+
21
+ Scenario: Staying in position during when the window is scrolled
22
+ Given there is enough space below the anchor to fit the SingleSelect menu
23
+ When the SingleSelect is clicked
24
+ And the window is scrolled down
25
+ Then the top of the menu is aligned with the bottom of the input
26
+ And the left of the SingleSelect is aligned with the left of the anchor
@@ -0,0 +1,9 @@
1
+ import { Given, Then } from '@badeball/cypress-cucumber-preprocessor'
2
+
3
+ Given('a SingleSelect with options and a selection is rendered', () => {
4
+ cy.visitStory('SingleSelect', 'With options and a selection')
5
+ })
6
+
7
+ Then('the selection is displayed', () => {
8
+ cy.contains('option one').should('be.visible')
9
+ })
@@ -0,0 +1,5 @@
1
+ Feature: Show current selections
2
+
3
+ Scenario: The singleselect has a selection
4
+ Given a SingleSelect with options and a selection is rendered
5
+ Then the selection is displayed
@@ -0,0 +1,41 @@
1
+ import PropTypes from 'prop-types'
2
+ import React from 'react'
3
+ import { FilterableMenu as CommonFilterableMenu } from '../select/index.js'
4
+ import { Menu } from './menu.js'
5
+
6
+ export const FilterableMenu = ({
7
+ dataTest,
8
+ options,
9
+ onChange,
10
+ selected,
11
+ empty,
12
+ handleClose,
13
+ handleFocusInput,
14
+ placeholder,
15
+ noMatchText,
16
+ }) => (
17
+ <CommonFilterableMenu
18
+ dataTest={dataTest}
19
+ options={options}
20
+ onChange={onChange}
21
+ selected={selected}
22
+ empty={empty}
23
+ handleClose={handleClose}
24
+ handleFocusInput={handleFocusInput}
25
+ placeholder={placeholder}
26
+ noMatchText={noMatchText}
27
+ Menu={Menu}
28
+ />
29
+ )
30
+
31
+ FilterableMenu.propTypes = {
32
+ dataTest: PropTypes.string.isRequired,
33
+ noMatchText: PropTypes.string.isRequired,
34
+ empty: PropTypes.node,
35
+ handleClose: PropTypes.func,
36
+ handleFocusInput: PropTypes.func,
37
+ options: PropTypes.node,
38
+ placeholder: PropTypes.string,
39
+ selected: PropTypes.string,
40
+ onChange: PropTypes.func,
41
+ }
@@ -0,0 +1 @@
1
+ export { SingleSelect } from './single-select.js'
@@ -0,0 +1,101 @@
1
+ import { requiredIf } from '@dhis2/prop-types'
2
+ import { colors } from '@dhis2/ui-constants'
3
+ import cx from 'classnames'
4
+ import PropTypes from 'prop-types'
5
+ import React from 'react'
6
+ import {
7
+ InputClearButton,
8
+ InputPlaceholder,
9
+ InputPrefix,
10
+ } from '../select/index.js'
11
+ import { Selection } from './selection.js'
12
+
13
+ const Input = ({
14
+ selected,
15
+ onChange,
16
+ clearable,
17
+ clearText,
18
+ placeholder,
19
+ dataTest,
20
+ prefix,
21
+ options,
22
+ className,
23
+ disabled,
24
+ inputMaxHeight = '100px',
25
+ }) => {
26
+ const hasSelection = selected && typeof selected === 'string'
27
+ const onClear = (e) => {
28
+ const data = { selected: '' }
29
+
30
+ e.stopPropagation()
31
+ onChange(data, e)
32
+ }
33
+
34
+ return (
35
+ <div className={cx('root', className)}>
36
+ <InputPrefix prefix={prefix} dataTest={`${dataTest}-prefix`} />
37
+ {!hasSelection && !prefix && (
38
+ <InputPlaceholder
39
+ placeholder={placeholder}
40
+ dataTest={`${dataTest}-placeholder`}
41
+ />
42
+ )}
43
+ {hasSelection && (
44
+ <div className="root-input">
45
+ {/* the wrapper div above is necessary to enforce wrapping on overflow */}
46
+ <Selection selected={selected} options={options} />
47
+ </div>
48
+ )}
49
+ {hasSelection && clearable && !disabled && (
50
+ <div className="root-right">
51
+ <InputClearButton
52
+ onClear={onClear}
53
+ clearText={clearText}
54
+ dataTest={`${dataTest}-clear`}
55
+ />
56
+ </div>
57
+ )}
58
+
59
+ <style jsx>{`
60
+ .root {
61
+ display: flex;
62
+ align-items: center;
63
+ color: ${colors.grey900};
64
+ font-size: 14px;
65
+ line-height: 16px;
66
+ }
67
+
68
+ .root-input {
69
+ overflow-y: auto;
70
+ flex: 1;
71
+ }
72
+
73
+ .root-right {
74
+ margin-inline-start: auto;
75
+ }
76
+ `}</style>
77
+
78
+ <style jsx>{`
79
+ .root-input {
80
+ max-height: ${inputMaxHeight};
81
+ }
82
+ `}</style>
83
+ </div>
84
+ )
85
+ }
86
+
87
+ Input.propTypes = {
88
+ dataTest: PropTypes.string.isRequired,
89
+ className: PropTypes.string,
90
+ clearText: requiredIf((props) => props.clearable, PropTypes.string),
91
+ clearable: PropTypes.bool,
92
+ disabled: PropTypes.bool,
93
+ inputMaxHeight: PropTypes.string,
94
+ options: PropTypes.node,
95
+ placeholder: PropTypes.string,
96
+ prefix: PropTypes.string,
97
+ selected: PropTypes.string,
98
+ onChange: PropTypes.func,
99
+ }
100
+
101
+ export { Input }