@wordpress/components 27.0.0 → 27.2.0

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 (314) hide show
  1. package/CHANGELOG.md +50 -2
  2. package/build/base-control/index.native.js.map +1 -1
  3. package/build/border-box-control/border-box-control/component.js.map +1 -1
  4. package/build/border-box-control/border-box-control-split-controls/component.js.map +1 -1
  5. package/build/border-control/border-control-dropdown/component.js.map +1 -1
  6. package/build/button/index.js +4 -5
  7. package/build/button/index.js.map +1 -1
  8. package/build/button/index.native.js.map +1 -1
  9. package/build/button/types.js.map +1 -1
  10. package/build/confirm-dialog/component.js.map +1 -1
  11. package/build/custom-select-control-v2/default-component/index.js.map +1 -1
  12. package/build/custom-select-control-v2/index.js +2 -2
  13. package/build/custom-select-control-v2/index.js.map +1 -1
  14. package/build/custom-select-control-v2/types.js.map +1 -1
  15. package/build/date-time/date/styles.js +7 -7
  16. package/build/date-time/date/styles.js.map +1 -1
  17. package/build/draggable/index.native.js +2 -2
  18. package/build/draggable/index.native.js.map +1 -1
  19. package/build/dropdown-menu/index.native.js.map +1 -1
  20. package/build/flex/flex/hook.js +1 -1
  21. package/build/flex/flex/hook.js.map +1 -1
  22. package/build/font-size-picker/index.native.js.map +1 -1
  23. package/build/form-token-field/index.js +1 -1
  24. package/build/form-token-field/index.js.map +1 -1
  25. package/build/h-stack/hook.js +6 -1
  26. package/build/h-stack/hook.js.map +1 -1
  27. package/build/input-control/index.js +1 -1
  28. package/build/input-control/index.js.map +1 -1
  29. package/build/mobile/bottom-sheet/button.native.js.map +1 -1
  30. package/build/mobile/bottom-sheet/index.native.js.map +1 -1
  31. package/build/mobile/bottom-sheet/range-cell.native.js.map +1 -1
  32. package/build/mobile/bottom-sheet/stepper-cell/index.native.js.map +1 -1
  33. package/build/mobile/bottom-sheet-select-control/index.native.js.map +1 -1
  34. package/build/mobile/bottom-sheet-text-control/index.native.js.map +1 -1
  35. package/build/mobile/gradient/index.native.js.map +1 -1
  36. package/build/mobile/image/index.native.js +4 -13
  37. package/build/mobile/image/index.native.js.map +1 -1
  38. package/build/mobile/media-edit/index.native.js.map +1 -1
  39. package/build/palette-edit/index.js +18 -12
  40. package/build/palette-edit/index.js.map +1 -1
  41. package/build/query-controls/index.native.js.map +1 -1
  42. package/build/range-control/index.js.map +1 -1
  43. package/build/search-control/index.native.js.map +1 -1
  44. package/build/snackbar/index.js +3 -2
  45. package/build/snackbar/index.js.map +1 -1
  46. package/build/snackbar/list.js +2 -1
  47. package/build/snackbar/list.js.map +1 -1
  48. package/build/snackbar/types.js.map +1 -1
  49. package/build/tabs/index.js +7 -7
  50. package/build/tabs/index.js.map +1 -1
  51. package/build/tabs/types.js.map +1 -1
  52. package/build/text-control/types.js.map +1 -1
  53. package/build/toggle-group-control/toggle-group-control-option-base/component.js +4 -1
  54. package/build/toggle-group-control/toggle-group-control-option-base/component.js.map +1 -1
  55. package/build/tools-panel/tools-panel-header/component.js +1 -1
  56. package/build/tools-panel/tools-panel-header/component.js.map +1 -1
  57. package/build/utils/config-values.js +1 -1
  58. package/build/utils/config-values.js.map +1 -1
  59. package/build/utils/hooks/index.js +0 -7
  60. package/build/utils/hooks/index.js.map +1 -1
  61. package/build/utils/input/base.js +2 -2
  62. package/build/utils/input/base.js.map +1 -1
  63. package/build-module/base-control/index.native.js.map +1 -1
  64. package/build-module/border-box-control/border-box-control/component.js.map +1 -1
  65. package/build-module/border-box-control/border-box-control-split-controls/component.js.map +1 -1
  66. package/build-module/border-control/border-control-dropdown/component.js.map +1 -1
  67. package/build-module/button/index.js +4 -5
  68. package/build-module/button/index.js.map +1 -1
  69. package/build-module/button/index.native.js.map +1 -1
  70. package/build-module/button/types.js.map +1 -1
  71. package/build-module/confirm-dialog/component.js.map +1 -1
  72. package/build-module/custom-select-control-v2/default-component/index.js.map +1 -1
  73. package/build-module/custom-select-control-v2/index.js +1 -1
  74. package/build-module/custom-select-control-v2/index.js.map +1 -1
  75. package/build-module/custom-select-control-v2/types.js.map +1 -1
  76. package/build-module/date-time/date/styles.js +7 -7
  77. package/build-module/date-time/date/styles.js.map +1 -1
  78. package/build-module/draggable/index.native.js +2 -2
  79. package/build-module/draggable/index.native.js.map +1 -1
  80. package/build-module/dropdown-menu/index.native.js.map +1 -1
  81. package/build-module/flex/flex/hook.js +1 -1
  82. package/build-module/flex/flex/hook.js.map +1 -1
  83. package/build-module/font-size-picker/index.native.js.map +1 -1
  84. package/build-module/form-token-field/index.js +1 -1
  85. package/build-module/form-token-field/index.js.map +1 -1
  86. package/build-module/h-stack/hook.js +6 -1
  87. package/build-module/h-stack/hook.js.map +1 -1
  88. package/build-module/input-control/index.js +1 -1
  89. package/build-module/input-control/index.js.map +1 -1
  90. package/build-module/mobile/bottom-sheet/button.native.js.map +1 -1
  91. package/build-module/mobile/bottom-sheet/index.native.js.map +1 -1
  92. package/build-module/mobile/bottom-sheet/range-cell.native.js.map +1 -1
  93. package/build-module/mobile/bottom-sheet/stepper-cell/index.native.js.map +1 -1
  94. package/build-module/mobile/bottom-sheet-select-control/index.native.js.map +1 -1
  95. package/build-module/mobile/bottom-sheet-text-control/index.native.js.map +1 -1
  96. package/build-module/mobile/gradient/index.native.js.map +1 -1
  97. package/build-module/mobile/image/index.native.js +6 -15
  98. package/build-module/mobile/image/index.native.js.map +1 -1
  99. package/build-module/mobile/media-edit/index.native.js.map +1 -1
  100. package/build-module/palette-edit/index.js +17 -11
  101. package/build-module/palette-edit/index.js.map +1 -1
  102. package/build-module/query-controls/index.native.js.map +1 -1
  103. package/build-module/range-control/index.js.map +1 -1
  104. package/build-module/search-control/index.native.js.map +1 -1
  105. package/build-module/snackbar/index.js +3 -2
  106. package/build-module/snackbar/index.js.map +1 -1
  107. package/build-module/snackbar/list.js +2 -1
  108. package/build-module/snackbar/list.js.map +1 -1
  109. package/build-module/snackbar/types.js.map +1 -1
  110. package/build-module/tabs/index.js +7 -7
  111. package/build-module/tabs/index.js.map +1 -1
  112. package/build-module/tabs/types.js.map +1 -1
  113. package/build-module/text-control/types.js.map +1 -1
  114. package/build-module/toggle-group-control/toggle-group-control-option-base/component.js +4 -1
  115. package/build-module/toggle-group-control/toggle-group-control-option-base/component.js.map +1 -1
  116. package/build-module/tools-panel/tools-panel-header/component.js +1 -1
  117. package/build-module/tools-panel/tools-panel-header/component.js.map +1 -1
  118. package/build-module/utils/config-values.js +1 -1
  119. package/build-module/utils/config-values.js.map +1 -1
  120. package/build-module/utils/hooks/index.js +0 -1
  121. package/build-module/utils/hooks/index.js.map +1 -1
  122. package/build-module/utils/input/base.js +2 -2
  123. package/build-module/utils/input/base.js.map +1 -1
  124. package/build-style/style-rtl.css +33 -23
  125. package/build-style/style.css +33 -23
  126. package/build-types/box-control/styles/box-control-styles.d.ts +1 -1
  127. package/build-types/button/deprecated.d.ts +4 -10
  128. package/build-types/button/deprecated.d.ts.map +1 -1
  129. package/build-types/button/index.d.ts +3 -3
  130. package/build-types/button/index.d.ts.map +1 -1
  131. package/build-types/button/stories/e2e/index.story.d.ts +1 -1
  132. package/build-types/button/stories/e2e/index.story.d.ts.map +1 -1
  133. package/build-types/button/stories/index.story.d.ts +7 -7
  134. package/build-types/button/stories/index.story.d.ts.map +1 -1
  135. package/build-types/button/types.d.ts +37 -8
  136. package/build-types/button/types.d.ts.map +1 -1
  137. package/build-types/color-picker/styles.d.ts +1 -1
  138. package/build-types/custom-select-control-v2/default-component/index.d.ts +2 -1
  139. package/build-types/custom-select-control-v2/default-component/index.d.ts.map +1 -1
  140. package/build-types/custom-select-control-v2/index.d.ts +1 -1
  141. package/build-types/custom-select-control-v2/index.d.ts.map +1 -1
  142. package/build-types/custom-select-control-v2/legacy-component/test/index.d.ts +2 -0
  143. package/build-types/custom-select-control-v2/legacy-component/test/index.d.ts.map +1 -0
  144. package/build-types/custom-select-control-v2/stories/default.story.d.ts +4 -3
  145. package/build-types/custom-select-control-v2/stories/default.story.d.ts.map +1 -1
  146. package/build-types/custom-select-control-v2/stories/legacy.story.d.ts +2 -2
  147. package/build-types/custom-select-control-v2/stories/legacy.story.d.ts.map +1 -1
  148. package/build-types/custom-select-control-v2/types.d.ts +0 -1
  149. package/build-types/custom-select-control-v2/types.d.ts.map +1 -1
  150. package/build-types/date-time/date/styles.d.ts +1 -1
  151. package/build-types/dropdown/stories/index.story.d.ts +1 -0
  152. package/build-types/dropdown/stories/index.story.d.ts.map +1 -1
  153. package/build-types/flex/flex/hook.d.ts +2 -3
  154. package/build-types/flex/flex/hook.d.ts.map +1 -1
  155. package/build-types/font-size-picker/styles.d.ts +1 -1
  156. package/build-types/form-token-field/index.d.ts +1 -1
  157. package/build-types/h-stack/hook.d.ts +2 -4
  158. package/build-types/h-stack/hook.d.ts.map +1 -1
  159. package/build-types/input-control/index.d.ts +1 -1
  160. package/build-types/input-control/stories/index.story.d.ts.map +1 -1
  161. package/build-types/navigation/stories/utils/hide-if-empty.d.ts.map +1 -1
  162. package/build-types/navigation/styles/navigation-styles.d.ts +1 -1
  163. package/build-types/navigator/navigator-back-button/component.d.ts +0 -1
  164. package/build-types/navigator/navigator-back-button/component.d.ts.map +1 -1
  165. package/build-types/navigator/navigator-back-button/hook.d.ts +1 -2
  166. package/build-types/navigator/navigator-back-button/hook.d.ts.map +1 -1
  167. package/build-types/navigator/navigator-button/component.d.ts +0 -1
  168. package/build-types/navigator/navigator-button/component.d.ts.map +1 -1
  169. package/build-types/navigator/navigator-button/hook.d.ts +1 -2
  170. package/build-types/navigator/navigator-button/hook.d.ts.map +1 -1
  171. package/build-types/navigator/navigator-to-parent-button/component.d.ts +0 -1
  172. package/build-types/navigator/navigator-to-parent-button/component.d.ts.map +1 -1
  173. package/build-types/number-control/styles/number-control-styles.d.ts +1 -1
  174. package/build-types/palette-edit/index.d.ts +6 -3
  175. package/build-types/palette-edit/index.d.ts.map +1 -1
  176. package/build-types/palette-edit/styles.d.ts +2 -2
  177. package/build-types/radio-group/stories/index.story.d.ts.map +1 -1
  178. package/build-types/snackbar/index.d.ts +5 -2
  179. package/build-types/snackbar/index.d.ts.map +1 -1
  180. package/build-types/snackbar/list.d.ts.map +1 -1
  181. package/build-types/snackbar/test/index.d.ts +2 -0
  182. package/build-types/snackbar/test/index.d.ts.map +1 -0
  183. package/build-types/snackbar/test/list.d.ts +2 -0
  184. package/build-types/snackbar/test/list.d.ts.map +1 -0
  185. package/build-types/snackbar/types.d.ts +18 -2
  186. package/build-types/snackbar/types.d.ts.map +1 -1
  187. package/build-types/tabs/index.d.ts +1 -1
  188. package/build-types/tabs/types.d.ts +1 -1
  189. package/build-types/text-control/index.d.ts +1 -1
  190. package/build-types/text-control/types.d.ts +1 -1
  191. package/build-types/text-control/types.d.ts.map +1 -1
  192. package/build-types/toggle-group-control/toggle-group-control-option-base/component.d.ts.map +1 -1
  193. package/build-types/toolbar/toolbar-button/index.d.ts +4 -10
  194. package/build-types/toolbar/toolbar-button/index.d.ts.map +1 -1
  195. package/build-types/utils/hooks/index.d.ts +0 -1
  196. package/build-types/v-stack/hook.d.ts +2 -4
  197. package/build-types/v-stack/hook.d.ts.map +1 -1
  198. package/package.json +19 -19
  199. package/src/base-control/index.native.js +1 -1
  200. package/src/base-control/test/index.tsx +1 -1
  201. package/src/border-box-control/border-box-control/component.tsx +1 -1
  202. package/src/border-box-control/border-box-control-split-controls/component.tsx +4 -4
  203. package/src/border-control/border-control-dropdown/component.tsx +1 -1
  204. package/src/button/index.native.js +1 -1
  205. package/src/button/index.tsx +3 -4
  206. package/src/button/style.scss +1 -3
  207. package/src/button/test/index.tsx +6 -6
  208. package/src/button/types.ts +37 -9
  209. package/src/circular-option-picker/test/index.tsx +2 -4
  210. package/src/combobox-control/test/index.tsx +1 -1
  211. package/src/confirm-dialog/README.md +7 -0
  212. package/src/confirm-dialog/component.tsx +1 -1
  213. package/src/confirm-dialog/test/index.tsx +5 -21
  214. package/src/custom-select-control-v2/default-component/index.tsx +4 -1
  215. package/src/custom-select-control-v2/index.tsx +1 -1
  216. package/src/custom-select-control-v2/legacy-component/test/index.tsx +457 -0
  217. package/src/custom-select-control-v2/stories/legacy.story.tsx +5 -6
  218. package/src/custom-select-control-v2/test/index.tsx +279 -749
  219. package/src/custom-select-control-v2/types.ts +0 -1
  220. package/src/date-time/date/styles.ts +2 -2
  221. package/src/dimension-control/test/__snapshots__/index.test.js.snap +4 -4
  222. package/src/disabled/test/index.tsx +1 -1
  223. package/src/draggable/index.native.js +2 -2
  224. package/src/draggable/test/index.native.js +6 -2
  225. package/src/dropdown/stories/index.story.tsx +19 -0
  226. package/src/dropdown/style.scss +26 -0
  227. package/src/dropdown-menu/index.native.js +2 -2
  228. package/src/dropdown-menu/style.scss +0 -25
  229. package/src/flex/flex/README.md +2 -2
  230. package/src/flex/flex/hook.ts +1 -1
  231. package/src/font-size-picker/index.native.js +2 -2
  232. package/src/form-token-field/README.md +1 -1
  233. package/src/form-token-field/index.tsx +2 -2
  234. package/src/grid/README.md +11 -11
  235. package/src/h-stack/README.md +6 -6
  236. package/src/h-stack/hook.tsx +2 -1
  237. package/src/h-stack/test/index.tsx +10 -0
  238. package/src/heading/README.md +1 -1
  239. package/src/heading/test/__snapshots__/index.tsx.snap +3 -3
  240. package/src/input-control/README.md +1 -1
  241. package/src/input-control/index.tsx +1 -1
  242. package/src/input-control/stories/index.story.tsx +1 -0
  243. package/src/item-group/test/__snapshots__/index.js.snap +11 -11
  244. package/src/item-group/test/index.js +2 -2
  245. package/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js +10 -15
  246. package/src/mobile/bottom-sheet/button.native.js +1 -5
  247. package/src/mobile/bottom-sheet/index.native.js +2 -2
  248. package/src/mobile/bottom-sheet/range-cell.native.js +1 -1
  249. package/src/mobile/bottom-sheet/stepper-cell/index.native.js +2 -2
  250. package/src/mobile/bottom-sheet-select-control/README.md +1 -1
  251. package/src/mobile/bottom-sheet-select-control/index.native.js +1 -1
  252. package/src/mobile/bottom-sheet-text-control/index.native.js +1 -1
  253. package/src/mobile/gradient/index.native.js +1 -1
  254. package/src/mobile/image/index.native.js +8 -23
  255. package/src/mobile/media-edit/index.native.js +1 -1
  256. package/src/modal/test/index.tsx +1 -1
  257. package/src/navigation/stories/utils/hide-if-empty.tsx +2 -6
  258. package/src/palette-edit/index.tsx +23 -23
  259. package/src/palette-edit/test/index.tsx +21 -17
  260. package/src/placeholder/style.scss +5 -1
  261. package/src/popover/test/index.tsx +1 -4
  262. package/src/progress-bar/README.md +1 -1
  263. package/src/query-controls/index.native.js +2 -2
  264. package/src/radio-control/README.md +3 -3
  265. package/src/radio-group/stories/index.story.tsx +1 -0
  266. package/src/range-control/index.tsx +3 -3
  267. package/src/range-control/test/index.tsx +2 -2
  268. package/src/resizable-box/resize-tooltip/README.md +2 -2
  269. package/src/search-control/index.native.js +1 -1
  270. package/src/snackbar/index.tsx +5 -2
  271. package/src/snackbar/list.tsx +6 -1
  272. package/src/snackbar/stories/list.story.tsx +0 -3
  273. package/src/snackbar/test/index.tsx +267 -0
  274. package/src/snackbar/test/list.tsx +46 -0
  275. package/src/snackbar/types.ts +31 -3
  276. package/src/tabs/README.md +18 -18
  277. package/src/tabs/index.tsx +7 -7
  278. package/src/tabs/stories/index.story.tsx +1 -1
  279. package/src/tabs/test/index.tsx +30 -30
  280. package/src/tabs/types.ts +1 -1
  281. package/src/text/test/__snapshots__/index.tsx.snap +3 -3
  282. package/src/text-control/types.ts +12 -1
  283. package/src/toggle-group-control/test/__snapshots__/index.tsx.snap +14 -10
  284. package/src/toggle-group-control/test/index.tsx +1 -1
  285. package/src/toggle-group-control/toggle-group-control-option-base/component.tsx +12 -10
  286. package/src/tools-panel/stories/index.story.tsx +8 -8
  287. package/src/tools-panel/test/index.tsx +10 -28
  288. package/src/tools-panel/tools-panel-header/component.tsx +1 -1
  289. package/src/tooltip/style.scss +2 -1
  290. package/src/tooltip/test/index.native.js +3 -3
  291. package/src/tree-grid/test/index.tsx +1 -1
  292. package/src/truncate/README.md +5 -5
  293. package/src/utils/config-values.js +1 -1
  294. package/src/utils/hooks/index.js +0 -1
  295. package/src/utils/input/base.js +1 -1
  296. package/src/v-stack/README.md +6 -6
  297. package/src/v-stack/test/index.tsx +10 -0
  298. package/tsconfig.json +1 -0
  299. package/tsconfig.tsbuildinfo +1 -1
  300. package/build/custom-select-control-v2/legacy-adapter.js +0 -29
  301. package/build/custom-select-control-v2/legacy-adapter.js.map +0 -1
  302. package/build/utils/hooks/use-latest-ref.js +0 -33
  303. package/build/utils/hooks/use-latest-ref.js.map +0 -1
  304. package/build-module/custom-select-control-v2/legacy-adapter.js +0 -21
  305. package/build-module/custom-select-control-v2/legacy-adapter.js.map +0 -1
  306. package/build-module/utils/hooks/use-latest-ref.js +0 -27
  307. package/build-module/utils/hooks/use-latest-ref.js.map +0 -1
  308. package/build-types/custom-select-control-v2/legacy-adapter.d.ts +0 -6
  309. package/build-types/custom-select-control-v2/legacy-adapter.d.ts.map +0 -1
  310. package/build-types/utils/hooks/use-latest-ref.d.ts +0 -15
  311. package/build-types/utils/hooks/use-latest-ref.d.ts.map +0 -1
  312. package/src/custom-select-control-v2/legacy-adapter.tsx +0 -25
  313. package/src/utils/hooks/test/use-latest-ref.js +0 -119
  314. package/src/utils/hooks/use-latest-ref.ts +0 -29
@@ -2,7 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { render, screen } from '@testing-library/react';
5
- import { click, press, sleep, type, waitFor } from '@ariakit/test';
5
+ import { click, press, sleep, type } from '@ariakit/test';
6
6
 
7
7
  /**
8
8
  * WordPress dependencies
@@ -13,100 +13,115 @@ import { useState } from '@wordpress/element';
13
13
  * Internal dependencies
14
14
  */
15
15
  import { CustomSelect as UncontrolledCustomSelect, CustomSelectItem } from '..';
16
- import type { CustomSelectProps, LegacyCustomSelectProps } from '../types';
16
+ import type { CustomSelectProps } from '../types';
17
+
18
+ const items = [
19
+ {
20
+ key: 'flower1',
21
+ value: 'violets',
22
+ },
23
+ {
24
+ key: 'flower2',
25
+ value: 'crimson clover',
26
+ },
27
+ {
28
+ key: 'flower3',
29
+ value: 'poppy',
30
+ },
31
+ {
32
+ key: 'color1',
33
+ value: 'amber',
34
+ },
35
+ {
36
+ key: 'color2',
37
+ value: 'aquamarine',
38
+ },
39
+ ];
17
40
 
18
- const customClass = 'amber-skies';
19
-
20
- const legacyProps = {
41
+ const defaultProps = {
21
42
  label: 'label!',
22
- options: [
23
- {
24
- key: 'flower1',
25
- name: 'violets',
26
- },
27
- {
28
- key: 'flower2',
29
- name: 'crimson clover',
30
- className: customClass,
31
- },
32
- {
33
- key: 'flower3',
34
- name: 'poppy',
35
- },
36
- {
37
- key: 'color1',
38
- name: 'amber',
39
- className: customClass,
40
- },
41
- {
42
- key: 'color2',
43
- name: 'aquamarine',
44
- style: {
45
- backgroundColor: 'rgb(127, 255, 212)',
46
- rotate: '13deg',
47
- },
48
- },
49
- ],
43
+ children: items.map( ( { value, key } ) => (
44
+ <CustomSelectItem value={ value } key={ key } />
45
+ ) ),
50
46
  };
51
47
 
52
- const LegacyControlledCustomSelect = ( {
53
- options,
54
- onChange,
55
- ...restProps
56
- }: LegacyCustomSelectProps ) => {
57
- const [ value, setValue ] = useState( options[ 0 ] );
48
+ const ControlledCustomSelect = ( props: CustomSelectProps ) => {
49
+ const [ value, setValue ] = useState< string | string[] >();
58
50
  return (
59
51
  <UncontrolledCustomSelect
60
- { ...restProps }
61
- options={ options }
62
- onChange={ ( args: any ) => {
63
- onChange?.( args );
64
- setValue( args.selectedItem );
52
+ { ...props }
53
+ onChange={ ( nextValue: string | string[] ) => {
54
+ setValue( nextValue );
55
+ props.onChange?.( nextValue );
65
56
  } }
66
- value={ options.find(
67
- ( option: any ) => option.key === value.key
68
- ) }
57
+ value={ value }
69
58
  />
70
59
  );
71
60
  };
72
61
 
73
- describe( 'With Legacy Props', () => {
74
- describe.each( [
75
- [ 'Uncontrolled', UncontrolledCustomSelect ],
76
- [ 'Controlled', LegacyControlledCustomSelect ],
77
- ] )( '%s', ( ...modeAndComponent ) => {
78
- const [ , Component ] = modeAndComponent;
62
+ describe.each( [
63
+ [ 'Uncontrolled', UncontrolledCustomSelect ],
64
+ [ 'Controlled', ControlledCustomSelect ],
65
+ ] )( 'CustomSelectControlV2 (%s)', ( ...modeAndComponent ) => {
66
+ const [ , Component ] = modeAndComponent;
79
67
 
80
- it( 'Should replace the initial selection when a new item is selected', async () => {
81
- render( <Component { ...legacyProps } /> );
68
+ it( 'Should replace the initial selection when a new item is selected', async () => {
69
+ render( <Component { ...defaultProps } /> );
82
70
 
83
- const currentSelectedItem = screen.getByRole( 'combobox', {
84
- expanded: false,
85
- } );
71
+ const currentSelectedItem = screen.getByRole( 'combobox', {
72
+ expanded: false,
73
+ } );
86
74
 
87
- await click( currentSelectedItem );
75
+ await click( currentSelectedItem );
88
76
 
89
- await click(
90
- screen.getByRole( 'option', {
91
- name: 'crimson clover',
92
- } )
93
- );
77
+ await click(
78
+ screen.getByRole( 'option', {
79
+ name: 'crimson clover',
80
+ } )
81
+ );
94
82
 
95
- expect( currentSelectedItem ).toHaveTextContent( 'crimson clover' );
83
+ expect( currentSelectedItem ).toHaveTextContent( 'crimson clover' );
96
84
 
97
- await click( currentSelectedItem );
85
+ await click( currentSelectedItem );
98
86
 
99
- await click(
100
- screen.getByRole( 'option', {
101
- name: 'poppy',
102
- } )
103
- );
87
+ await click(
88
+ screen.getByRole( 'option', {
89
+ name: 'poppy',
90
+ } )
91
+ );
92
+
93
+ expect( currentSelectedItem ).toHaveTextContent( 'poppy' );
94
+ } );
95
+
96
+ it( 'Should keep current selection if dropdown is closed without changing selection', async () => {
97
+ render( <Component { ...defaultProps } /> );
104
98
 
105
- expect( currentSelectedItem ).toHaveTextContent( 'poppy' );
99
+ const currentSelectedItem = screen.getByRole( 'combobox', {
100
+ expanded: false,
106
101
  } );
107
102
 
108
- it( 'Should keep current selection if dropdown is closed without changing selection', async () => {
109
- render( <Component { ...legacyProps } /> );
103
+ await sleep();
104
+ await press.Tab();
105
+ await press.Enter();
106
+ expect(
107
+ screen.getByRole( 'listbox', {
108
+ name: 'label!',
109
+ } )
110
+ ).toBeVisible();
111
+
112
+ await press.Escape();
113
+ expect(
114
+ screen.queryByRole( 'listbox', {
115
+ name: 'label!',
116
+ } )
117
+ ).not.toBeInTheDocument();
118
+
119
+ expect( currentSelectedItem ).toHaveTextContent( items[ 0 ].value );
120
+ } );
121
+
122
+ describe( 'Keyboard behavior and accessibility', () => {
123
+ it( 'Should be able to change selection using keyboard', async () => {
124
+ render( <Component { ...defaultProps } /> );
110
125
 
111
126
  const currentSelectedItem = screen.getByRole( 'combobox', {
112
127
  expanded: false,
@@ -114,417 +129,67 @@ describe( 'With Legacy Props', () => {
114
129
 
115
130
  await sleep();
116
131
  await press.Tab();
132
+ expect( currentSelectedItem ).toHaveFocus();
133
+
117
134
  await press.Enter();
118
135
  expect(
119
136
  screen.getByRole( 'listbox', {
120
137
  name: 'label!',
121
138
  } )
122
- ).toBeVisible();
123
-
124
- await press.Escape();
125
- expect(
126
- screen.queryByRole( 'listbox', {
127
- name: 'label!',
128
- } )
129
- ).not.toBeInTheDocument();
130
-
131
- expect( currentSelectedItem ).toHaveTextContent(
132
- legacyProps.options[ 0 ].name
133
- );
134
- } );
135
-
136
- it( 'Should apply class only to options that have a className defined', async () => {
137
- render( <Component { ...legacyProps } /> );
138
-
139
- await click(
140
- screen.getByRole( 'combobox', {
141
- expanded: false,
142
- } )
143
- );
144
-
145
- // return an array of items _with_ a className added
146
- const itemsWithClass = legacyProps.options.filter(
147
- ( option ) => option.className !== undefined
148
- );
149
-
150
- // assert against filtered array
151
- itemsWithClass.map( ( { name } ) =>
152
- expect( screen.getByRole( 'option', { name } ) ).toHaveClass(
153
- customClass
154
- )
155
- );
156
-
157
- // return an array of items _without_ a className added
158
- const itemsWithoutClass = legacyProps.options.filter(
159
- ( option ) => option.className === undefined
160
- );
161
-
162
- // assert against filtered array
163
- itemsWithoutClass.map( ( { name } ) =>
164
- expect(
165
- screen.getByRole( 'option', { name } )
166
- ).not.toHaveClass( customClass )
167
- );
168
- } );
169
-
170
- it( 'Should apply styles only to options that have styles defined', async () => {
171
- const customStyles =
172
- 'background-color: rgb(127, 255, 212); rotate: 13deg;';
173
-
174
- render( <Component { ...legacyProps } /> );
175
-
176
- await click(
177
- screen.getByRole( 'combobox', {
178
- expanded: false,
179
- } )
180
- );
181
-
182
- // return an array of items _with_ styles added
183
- const styledItems = legacyProps.options.filter(
184
- ( option ) => option.style !== undefined
185
- );
186
-
187
- // assert against filtered array
188
- styledItems.map( ( { name } ) =>
189
- expect( screen.getByRole( 'option', { name } ) ).toHaveStyle(
190
- customStyles
191
- )
192
- );
193
-
194
- // return an array of items _without_ styles added
195
- const unstyledItems = legacyProps.options.filter(
196
- ( option ) => option.style === undefined
197
- );
198
-
199
- // assert against filtered array
200
- unstyledItems.map( ( { name } ) =>
201
- expect(
202
- screen.getByRole( 'option', { name } )
203
- ).not.toHaveStyle( customStyles )
204
- );
205
- } );
206
-
207
- it( 'does not show selected hint by default', async () => {
208
- render(
209
- <Component
210
- { ...legacyProps }
211
- label="Custom select"
212
- options={ [
213
- {
214
- key: 'one',
215
- name: 'One',
216
- __experimentalHint: 'Hint',
217
- },
218
- ] }
219
- />
220
- );
221
- await waitFor( () =>
222
- expect(
223
- screen.getByRole( 'combobox', { name: 'Custom select' } )
224
- ).not.toHaveTextContent( 'Hint' )
225
- );
226
- } );
227
-
228
- it( 'shows selected hint when __experimentalShowSelectedHint is set', async () => {
229
- render(
230
- <Component
231
- { ...legacyProps }
232
- label="Custom select"
233
- options={ [
234
- {
235
- key: 'one',
236
- name: 'One',
237
- __experimentalHint: 'Hint',
238
- },
239
- ] }
240
- __experimentalShowSelectedHint
241
- />
242
- );
243
-
244
- await waitFor( () =>
245
- expect(
246
- screen.getByRole( 'combobox', {
247
- expanded: false,
248
- } )
249
- ).toHaveTextContent( /hint/i )
250
- );
251
- } );
252
-
253
- it( 'shows selected hint in list of options when added', async () => {
254
- render(
255
- <Component
256
- { ...legacyProps }
257
- label="Custom select"
258
- options={ [
259
- {
260
- key: 'one',
261
- name: 'One',
262
- __experimentalHint: 'Hint',
263
- },
264
- ] }
265
- __experimentalShowSelectedHint
266
- />
267
- );
268
-
269
- await click(
270
- screen.getByRole( 'combobox', { name: 'Custom select' } )
271
- );
272
-
273
- expect(
274
- screen.getByRole( 'option', { name: /hint/i } )
275
- ).toBeVisible();
276
- } );
277
-
278
- it( 'Should return object onChange', async () => {
279
- const mockOnChange = jest.fn();
280
-
281
- render(
282
- <Component { ...legacyProps } onChange={ mockOnChange } />
283
- );
284
-
285
- await click(
286
- screen.getByRole( 'combobox', {
287
- expanded: false,
288
- } )
289
- );
290
-
291
- expect( mockOnChange ).toHaveBeenNthCalledWith(
292
- 1,
293
- expect.objectContaining( {
294
- inputValue: '',
295
- isOpen: false,
296
- selectedItem: { key: 'violets', name: 'violets' },
297
- type: '',
298
- } )
299
- );
139
+ ).toHaveFocus();
300
140
 
301
- await click(
302
- screen.getByRole( 'option', {
303
- name: 'aquamarine',
304
- } )
305
- );
141
+ await press.ArrowDown();
142
+ await press.Enter();
306
143
 
307
- expect( mockOnChange ).toHaveBeenNthCalledWith(
308
- 2,
309
- expect.objectContaining( {
310
- inputValue: '',
311
- isOpen: false,
312
- selectedItem: expect.objectContaining( {
313
- name: 'aquamarine',
314
- } ),
315
- type: '',
316
- } )
317
- );
144
+ expect( currentSelectedItem ).toHaveTextContent( 'crimson clover' );
318
145
  } );
319
146
 
320
- it( 'Should return selectedItem object when specified onChange', async () => {
321
- const mockOnChange = jest.fn(
322
- ( { selectedItem } ) => selectedItem.key
323
- );
147
+ it( 'Should be able to type characters to select matching options', async () => {
148
+ render( <Component { ...defaultProps } /> );
324
149
 
325
- render(
326
- <Component { ...legacyProps } onChange={ mockOnChange } />
327
- );
150
+ const currentSelectedItem = screen.getByRole( 'combobox', {
151
+ expanded: false,
152
+ } );
328
153
 
329
154
  await sleep();
330
155
  await press.Tab();
156
+ await press.Enter();
331
157
  expect(
332
- screen.getByRole( 'combobox', {
333
- expanded: false,
158
+ screen.getByRole( 'listbox', {
159
+ name: 'label!',
334
160
  } )
335
161
  ).toHaveFocus();
336
162
 
337
- await type( 'p' );
163
+ await type( 'a' );
338
164
  await press.Enter();
339
-
340
- expect( mockOnChange ).toHaveReturnedWith( 'poppy' );
165
+ expect( currentSelectedItem ).toHaveTextContent( 'amber' );
341
166
  } );
342
167
 
343
- describe( 'Keyboard behavior and accessibility', () => {
344
- it( 'Should be able to change selection using keyboard', async () => {
345
- render( <Component { ...legacyProps } /> );
346
-
347
- const currentSelectedItem = screen.getByRole( 'combobox', {
348
- expanded: false,
349
- } );
350
-
351
- await sleep();
352
- await press.Tab();
353
- expect( currentSelectedItem ).toHaveFocus();
354
-
355
- await press.Enter();
356
- expect(
357
- screen.getByRole( 'listbox', {
358
- name: 'label!',
359
- } )
360
- ).toHaveFocus();
361
-
362
- await press.ArrowDown();
363
- await press.Enter();
364
-
365
- expect( currentSelectedItem ).toHaveTextContent(
366
- 'crimson clover'
367
- );
368
- } );
369
-
370
- it( 'Should be able to type characters to select matching options', async () => {
371
- render( <Component { ...legacyProps } /> );
372
-
373
- const currentSelectedItem = screen.getByRole( 'combobox', {
374
- expanded: false,
375
- } );
376
-
377
- await sleep();
378
- await press.Tab();
379
- await press.Enter();
380
- expect(
381
- screen.getByRole( 'listbox', {
382
- name: 'label!',
383
- } )
384
- ).toHaveFocus();
385
-
386
- await type( 'a' );
387
- await press.Enter();
388
- expect( currentSelectedItem ).toHaveTextContent( 'amber' );
389
- } );
390
-
391
- it( 'Can change selection with a focused input and closed dropdown if typed characters match an option', async () => {
392
- render( <Component { ...legacyProps } /> );
393
-
394
- const currentSelectedItem = screen.getByRole( 'combobox', {
395
- expanded: false,
396
- } );
397
-
398
- await sleep();
399
- await press.Tab();
400
- expect( currentSelectedItem ).toHaveFocus();
401
-
402
- await type( 'aq' );
403
-
404
- expect(
405
- screen.queryByRole( 'listbox', {
406
- name: 'label!',
407
- hidden: true,
408
- } )
409
- ).not.toBeInTheDocument();
168
+ it( 'Can change selection with a focused input and closed dropdown if typed characters match an option', async () => {
169
+ render( <Component { ...defaultProps } /> );
410
170
 
411
- await press.Enter();
412
- expect( currentSelectedItem ).toHaveTextContent( 'aquamarine' );
171
+ const currentSelectedItem = screen.getByRole( 'combobox', {
172
+ expanded: false,
413
173
  } );
414
174
 
415
- it( 'Should have correct aria-selected value for selections', async () => {
416
- render( <Component { ...legacyProps } /> );
417
-
418
- const currentSelectedItem = screen.getByRole( 'combobox', {
419
- expanded: false,
420
- } );
421
-
422
- await click( currentSelectedItem );
423
-
424
- // get all items except for first option
425
- const unselectedItems = legacyProps.options.filter(
426
- ( { name } ) => name !== legacyProps.options[ 0 ].name
427
- );
428
-
429
- // assert that all other items have aria-selected="false"
430
- unselectedItems.map( ( { name } ) =>
431
- expect(
432
- screen.getByRole( 'option', { name, selected: false } )
433
- ).toBeVisible()
434
- );
435
-
436
- // assert that first item has aria-selected="true"
437
- expect(
438
- screen.getByRole( 'option', {
439
- name: legacyProps.options[ 0 ].name,
440
- selected: true,
441
- } )
442
- ).toBeVisible();
443
-
444
- // change the current selection
445
- await click( screen.getByRole( 'option', { name: 'poppy' } ) );
175
+ await sleep();
176
+ await press.Tab();
177
+ expect( currentSelectedItem ).toHaveFocus();
446
178
 
447
- // click combobox to mount listbox with options again
448
- await click( currentSelectedItem );
179
+ await type( 'aq' );
449
180
 
450
- // check that first item is has aria-selected="false" after new selection
451
- expect(
452
- screen.getByRole( 'option', {
453
- name: legacyProps.options[ 0 ].name,
454
- selected: false,
455
- } )
456
- ).toBeVisible();
181
+ expect(
182
+ screen.queryByRole( 'listbox', {
183
+ name: 'label!',
184
+ hidden: true,
185
+ } )
186
+ ).not.toBeInTheDocument();
457
187
 
458
- // check that new selected item now has aria-selected="true"
459
- expect(
460
- screen.getByRole( 'option', {
461
- name: 'poppy',
462
- selected: true,
463
- } )
464
- ).toBeVisible();
465
- } );
188
+ await press.Enter();
189
+ expect( currentSelectedItem ).toHaveTextContent( 'aquamarine' );
466
190
  } );
467
- } );
468
- } );
469
-
470
- describe( 'static typing', () => {
471
- <>
472
- { /* @ts-expect-error - when `options` prop is passed, `onChange` should have legacy signature */ }
473
- <UncontrolledCustomSelect
474
- label="foo"
475
- options={ [] }
476
- onChange={ ( _: string | string[] ) => undefined }
477
- />
478
- <UncontrolledCustomSelect
479
- label="foo"
480
- options={ [] }
481
- onChange={ ( _: { selectedItem: unknown } ) => undefined }
482
- />
483
- <UncontrolledCustomSelect
484
- label="foo"
485
- onChange={ ( _: string | string[] ) => undefined }
486
- >
487
- foobar
488
- </UncontrolledCustomSelect>
489
- { /* @ts-expect-error - when `children` are passed, `onChange` should have new default signature */ }
490
- <UncontrolledCustomSelect
491
- label="foo"
492
- onChange={ ( _: { selectedItem: unknown } ) => undefined }
493
- >
494
- foobar
495
- </UncontrolledCustomSelect>
496
- </>;
497
- } );
498
-
499
- const defaultProps = {
500
- label: 'label!',
501
- children: legacyProps.options.map( ( { name, key } ) => (
502
- <CustomSelectItem value={ name } key={ key } />
503
- ) ),
504
- };
505
-
506
- const ControlledCustomSelect = ( props: CustomSelectProps ) => {
507
- const [ value, setValue ] = useState< string | string[] >();
508
- return (
509
- <UncontrolledCustomSelect
510
- { ...props }
511
- onChange={ ( nextValue: string | string[] ) => {
512
- setValue( nextValue );
513
- props.onChange?.( nextValue );
514
- } }
515
- value={ value }
516
- />
517
- );
518
- };
519
191
 
520
- describe( 'With Default Props', () => {
521
- describe.each( [
522
- [ 'Uncontrolled', UncontrolledCustomSelect ],
523
- [ 'Controlled', ControlledCustomSelect ],
524
- ] )( '%s', ( ...modeAndComponent ) => {
525
- const [ , Component ] = modeAndComponent;
526
-
527
- it( 'Should replace the initial selection when a new item is selected', async () => {
192
+ it( 'Should have correct aria-selected value for selections', async () => {
528
193
  render( <Component { ...defaultProps } /> );
529
194
 
530
195
  const currentSelectedItem = screen.getByRole( 'combobox', {
@@ -533,320 +198,134 @@ describe( 'With Default Props', () => {
533
198
 
534
199
  await click( currentSelectedItem );
535
200
 
536
- await click(
201
+ // assert that first item has aria-selected="true"
202
+ expect(
537
203
  screen.getByRole( 'option', {
538
- name: 'crimson clover',
204
+ name: 'violets',
205
+ selected: true,
539
206
  } )
540
- );
207
+ ).toBeVisible();
541
208
 
542
- expect( currentSelectedItem ).toHaveTextContent( 'crimson clover' );
209
+ // change the current selection
210
+ await click( screen.getByRole( 'option', { name: 'poppy' } ) );
543
211
 
212
+ // click combobox to mount listbox with options again
544
213
  await click( currentSelectedItem );
545
214
 
546
- await click(
547
- screen.getByRole( 'option', {
548
- name: 'poppy',
549
- } )
550
- );
551
-
552
- expect( currentSelectedItem ).toHaveTextContent( 'poppy' );
553
- } );
554
-
555
- it( 'Should keep current selection if dropdown is closed without changing selection', async () => {
556
- render( <Component { ...defaultProps } /> );
557
-
558
- const currentSelectedItem = screen.getByRole( 'combobox', {
559
- expanded: false,
560
- } );
561
-
562
- await sleep();
563
- await press.Tab();
564
- await press.Enter();
215
+ // check that first item is has aria-selected="false" after new selection
565
216
  expect(
566
- screen.getByRole( 'listbox', {
567
- name: 'label!',
217
+ screen.getByRole( 'option', {
218
+ name: 'violets',
219
+ selected: false,
568
220
  } )
569
221
  ).toBeVisible();
570
222
 
571
- await press.Escape();
223
+ // check that new selected item now has aria-selected="true"
572
224
  expect(
573
- screen.queryByRole( 'listbox', {
574
- name: 'label!',
225
+ screen.getByRole( 'option', {
226
+ name: 'poppy',
227
+ selected: true,
575
228
  } )
576
- ).not.toBeInTheDocument();
577
-
578
- expect( currentSelectedItem ).toHaveTextContent(
579
- legacyProps.options[ 0 ].name
580
- );
229
+ ).toBeVisible();
581
230
  } );
231
+ } );
582
232
 
583
- describe( 'Keyboard behavior and accessibility', () => {
584
- it( 'Should be able to change selection using keyboard', async () => {
585
- render( <Component { ...defaultProps } /> );
586
-
587
- const currentSelectedItem = screen.getByRole( 'combobox', {
588
- expanded: false,
589
- } );
590
-
591
- await sleep();
592
- await press.Tab();
593
- expect( currentSelectedItem ).toHaveFocus();
594
-
595
- await press.Enter();
596
- expect(
597
- screen.getByRole( 'listbox', {
598
- name: 'label!',
599
- } )
600
- ).toHaveFocus();
601
-
602
- await press.ArrowDown();
603
- await press.Enter();
604
-
605
- expect( currentSelectedItem ).toHaveTextContent(
606
- 'crimson clover'
607
- );
608
- } );
609
-
610
- it( 'Should be able to type characters to select matching options', async () => {
611
- render( <Component { ...defaultProps } /> );
612
-
613
- const currentSelectedItem = screen.getByRole( 'combobox', {
614
- expanded: false,
615
- } );
616
-
617
- await sleep();
618
- await press.Tab();
619
- await press.Enter();
620
- expect(
621
- screen.getByRole( 'listbox', {
622
- name: 'label!',
623
- } )
624
- ).toHaveFocus();
625
-
626
- await type( 'a' );
627
- await press.Enter();
628
- expect( currentSelectedItem ).toHaveTextContent( 'amber' );
629
- } );
630
-
631
- it( 'Can change selection with a focused input and closed dropdown if typed characters match an option', async () => {
632
- render( <Component { ...defaultProps } /> );
633
-
634
- const currentSelectedItem = screen.getByRole( 'combobox', {
635
- expanded: false,
636
- } );
637
-
638
- await sleep();
639
- await press.Tab();
640
- expect( currentSelectedItem ).toHaveFocus();
233
+ describe( 'Multiple selection', () => {
234
+ it( 'Should be able to select multiple items when provided an array', async () => {
235
+ const onChangeMock = jest.fn();
641
236
 
642
- await type( 'aq' );
237
+ // initial selection as defaultValue
238
+ const defaultValues = [
239
+ 'incandescent glow',
240
+ 'ultraviolet morning light',
241
+ ];
643
242
 
644
- expect(
645
- screen.queryByRole( 'listbox', {
646
- name: 'label!',
647
- hidden: true,
648
- } )
649
- ).not.toBeInTheDocument();
243
+ render(
244
+ <Component
245
+ defaultValue={ defaultValues }
246
+ onChange={ onChangeMock }
247
+ label="Multi-select"
248
+ >
249
+ { [
250
+ 'aurora borealis green',
251
+ 'flamingo pink sunrise',
252
+ 'incandescent glow',
253
+ 'rose blush',
254
+ 'ultraviolet morning light',
255
+ ].map( ( item ) => (
256
+ <CustomSelectItem key={ item } value={ item }>
257
+ { item }
258
+ </CustomSelectItem>
259
+ ) ) }
260
+ </Component>
261
+ );
650
262
 
651
- await press.Enter();
652
- expect( currentSelectedItem ).toHaveTextContent( 'aquamarine' );
263
+ const currentSelectedItem = screen.getByRole( 'combobox', {
264
+ expanded: false,
653
265
  } );
654
266
 
655
- it( 'Should have correct aria-selected value for selections', async () => {
656
- render( <Component { ...defaultProps } /> );
267
+ // ensure more than one item is selected due to defaultValues
268
+ expect( currentSelectedItem ).toHaveTextContent(
269
+ `${ defaultValues.length } items selected`
270
+ );
657
271
 
658
- const currentSelectedItem = screen.getByRole( 'combobox', {
659
- expanded: false,
660
- } );
272
+ await click( currentSelectedItem );
661
273
 
662
- await click( currentSelectedItem );
274
+ expect( screen.getByRole( 'listbox' ) ).toHaveAttribute(
275
+ 'aria-multiselectable'
276
+ );
663
277
 
664
- // assert that first item has aria-selected="true"
278
+ // ensure defaultValues are selected in list of items
279
+ defaultValues.forEach( ( value ) =>
665
280
  expect(
666
281
  screen.getByRole( 'option', {
667
- name: 'violets',
282
+ name: value,
668
283
  selected: true,
669
284
  } )
670
- ).toBeVisible();
671
-
672
- // change the current selection
673
- await click( screen.getByRole( 'option', { name: 'poppy' } ) );
674
-
675
- // click combobox to mount listbox with options again
676
- await click( currentSelectedItem );
285
+ ).toBeVisible()
286
+ );
677
287
 
678
- // check that first item is has aria-selected="false" after new selection
679
- expect(
680
- screen.getByRole( 'option', {
681
- name: 'violets',
682
- selected: false,
683
- } )
684
- ).toBeVisible();
288
+ // name of next selection
289
+ const nextSelectionName = 'rose blush';
685
290
 
686
- // check that new selected item now has aria-selected="true"
687
- expect(
688
- screen.getByRole( 'option', {
689
- name: 'poppy',
690
- selected: true,
691
- } )
692
- ).toBeVisible();
291
+ // element for next selection
292
+ const nextSelection = screen.getByRole( 'option', {
293
+ name: nextSelectionName,
693
294
  } );
694
- } );
695
-
696
- describe( 'Multiple selection', () => {
697
- it( 'Should be able to select multiple items when provided an array', async () => {
698
- const onChangeMock = jest.fn();
699
-
700
- // initial selection as defaultValue
701
- const defaultValues = [
702
- 'incandescent glow',
703
- 'ultraviolet morning light',
704
- ];
705
-
706
- render(
707
- <Component
708
- defaultValue={ defaultValues }
709
- onChange={ onChangeMock }
710
- label="Multi-select"
711
- >
712
- { [
713
- 'aurora borealis green',
714
- 'flamingo pink sunrise',
715
- 'incandescent glow',
716
- 'rose blush',
717
- 'ultraviolet morning light',
718
- ].map( ( item ) => (
719
- <CustomSelectItem key={ item } value={ item }>
720
- { item }
721
- </CustomSelectItem>
722
- ) ) }
723
- </Component>
724
- );
725
-
726
- const currentSelectedItem = screen.getByRole( 'combobox', {
727
- expanded: false,
728
- } );
729
-
730
- // ensure more than one item is selected due to defaultValues
731
- expect( currentSelectedItem ).toHaveTextContent(
732
- `${ defaultValues.length } items selected`
733
- );
734
-
735
- await click( currentSelectedItem );
736
-
737
- expect( screen.getByRole( 'listbox' ) ).toHaveAttribute(
738
- 'aria-multiselectable'
739
- );
740
-
741
- // ensure defaultValues are selected in list of items
742
- defaultValues.forEach( ( value ) =>
743
- expect(
744
- screen.getByRole( 'option', {
745
- name: value,
746
- selected: true,
747
- } )
748
- ).toBeVisible()
749
- );
750
-
751
- // name of next selection
752
- const nextSelectionName = 'rose blush';
753
-
754
- // element for next selection
755
- const nextSelection = screen.getByRole( 'option', {
756
- name: nextSelectionName,
757
- } );
758
295
 
759
- // click next selection to add another item to current selection
760
- await click( nextSelection );
296
+ // click next selection to add another item to current selection
297
+ await click( nextSelection );
761
298
 
762
- // updated array containing defaultValues + the item just selected
763
- const updatedSelection =
764
- defaultValues.concat( nextSelectionName );
299
+ // updated array containing defaultValues + the item just selected
300
+ const updatedSelection = defaultValues.concat( nextSelectionName );
765
301
 
766
- expect( onChangeMock ).toHaveBeenCalledWith( updatedSelection );
302
+ expect( onChangeMock ).toHaveBeenCalledWith( updatedSelection );
767
303
 
768
- expect( nextSelection ).toHaveAttribute( 'aria-selected' );
304
+ expect( nextSelection ).toHaveAttribute( 'aria-selected' );
769
305
 
770
- // expect increased array length for current selection
771
- expect( currentSelectedItem ).toHaveTextContent(
772
- `${ updatedSelection.length } items selected`
773
- );
774
- } );
775
-
776
- it( 'Should be able to deselect items when provided an array', async () => {
777
- // initial selection as defaultValue
778
- const defaultValues = [
779
- 'aurora borealis green',
780
- 'incandescent glow',
781
- 'key lime green',
782
- 'rose blush',
783
- 'ultraviolet morning light',
784
- ];
785
-
786
- render(
787
- <Component
788
- defaultValue={ defaultValues }
789
- label="Multi-select"
790
- >
791
- { defaultValues.map( ( item ) => (
792
- <CustomSelectItem key={ item } value={ item }>
793
- { item }
794
- </CustomSelectItem>
795
- ) ) }
796
- </Component>
797
- );
798
-
799
- const currentSelectedItem = screen.getByRole( 'combobox', {
800
- expanded: false,
801
- } );
802
-
803
- await click( currentSelectedItem );
804
-
805
- // Array containing items to deselect
806
- const nextSelection = [
807
- 'aurora borealis green',
808
- 'rose blush',
809
- 'incandescent glow',
810
- ];
811
-
812
- // Deselect some items by clicking them to ensure that changes
813
- // are reflected correctly
814
- await Promise.all(
815
- nextSelection.map( async ( value ) => {
816
- await click(
817
- screen.getByRole( 'option', { name: value } )
818
- );
819
- expect(
820
- screen.getByRole( 'option', {
821
- name: value,
822
- selected: false,
823
- } )
824
- ).toBeVisible();
825
- } )
826
- );
827
-
828
- // expect different array length from defaultValues due to deselecting items
829
- expect( currentSelectedItem ).toHaveTextContent(
830
- `${
831
- defaultValues.length - nextSelection.length
832
- } items selected`
833
- );
834
- } );
306
+ // expect increased array length for current selection
307
+ expect( currentSelectedItem ).toHaveTextContent(
308
+ `${ updatedSelection.length } items selected`
309
+ );
835
310
  } );
836
311
 
837
- it( 'Should allow rendering a custom value when using `renderSelectedValue`', async () => {
838
- const renderValue = ( value: string | string[] ) => {
839
- return <img src={ `${ value }.jpg` } alt={ value as string } />;
840
- };
312
+ it( 'Should be able to deselect items when provided an array', async () => {
313
+ // initial selection as defaultValue
314
+ const defaultValues = [
315
+ 'aurora borealis green',
316
+ 'incandescent glow',
317
+ 'key lime green',
318
+ 'rose blush',
319
+ 'ultraviolet morning light',
320
+ ];
841
321
 
842
322
  render(
843
- <Component label="Rendered" renderSelectedValue={ renderValue }>
844
- <CustomSelectItem value="april-29">
845
- { renderValue( 'april-29' ) }
846
- </CustomSelectItem>
847
- <CustomSelectItem value="july-9">
848
- { renderValue( 'july-9' ) }
849
- </CustomSelectItem>
323
+ <Component defaultValue={ defaultValues } label="Multi-select">
324
+ { defaultValues.map( ( item ) => (
325
+ <CustomSelectItem key={ item } value={ item }>
326
+ { item }
327
+ </CustomSelectItem>
328
+ ) ) }
850
329
  </Component>
851
330
  );
852
331
 
@@ -854,26 +333,77 @@ describe( 'With Default Props', () => {
854
333
  expanded: false,
855
334
  } );
856
335
 
857
- expect( currentSelectedItem ).toBeVisible();
336
+ await click( currentSelectedItem );
858
337
 
859
- // expect that the initial selection renders an image
860
- expect( currentSelectedItem ).toContainElement(
861
- screen.getByRole( 'img', { name: 'april-29' } )
338
+ // Array containing items to deselect
339
+ const nextSelection = [
340
+ 'aurora borealis green',
341
+ 'rose blush',
342
+ 'incandescent glow',
343
+ ];
344
+
345
+ // Deselect some items by clicking them to ensure that changes
346
+ // are reflected correctly
347
+ await Promise.all(
348
+ nextSelection.map( async ( value ) => {
349
+ await click(
350
+ screen.getByRole( 'option', { name: value } )
351
+ );
352
+ expect(
353
+ screen.getByRole( 'option', {
354
+ name: value,
355
+ selected: false,
356
+ } )
357
+ ).toBeVisible();
358
+ } )
862
359
  );
863
360
 
864
- expect(
865
- screen.queryByRole( 'img', { name: 'july-9' } )
866
- ).not.toBeInTheDocument();
867
-
868
- await click( currentSelectedItem );
361
+ // expect different array length from defaultValues due to deselecting items
362
+ expect( currentSelectedItem ).toHaveTextContent(
363
+ `${
364
+ defaultValues.length - nextSelection.length
365
+ } items selected`
366
+ );
367
+ } );
368
+ } );
869
369
 
870
- // expect that the other image is only visible after opening popover with options
871
- expect(
872
- screen.getByRole( 'img', { name: 'july-9' } )
873
- ).toBeVisible();
874
- expect(
875
- screen.getByRole( 'option', { name: 'july-9' } )
876
- ).toBeVisible();
370
+ it( 'Should allow rendering a custom value when using `renderSelectedValue`', async () => {
371
+ const renderValue = ( value: string | string[] ) => {
372
+ return <img src={ `${ value }.jpg` } alt={ value as string } />;
373
+ };
374
+
375
+ render(
376
+ <Component label="Rendered" renderSelectedValue={ renderValue }>
377
+ <CustomSelectItem value="april-29">
378
+ { renderValue( 'april-29' ) }
379
+ </CustomSelectItem>
380
+ <CustomSelectItem value="july-9">
381
+ { renderValue( 'july-9' ) }
382
+ </CustomSelectItem>
383
+ </Component>
384
+ );
385
+
386
+ const currentSelectedItem = screen.getByRole( 'combobox', {
387
+ expanded: false,
877
388
  } );
389
+
390
+ expect( currentSelectedItem ).toBeVisible();
391
+
392
+ // expect that the initial selection renders an image
393
+ expect( currentSelectedItem ).toContainElement(
394
+ screen.getByRole( 'img', { name: 'april-29' } )
395
+ );
396
+
397
+ expect(
398
+ screen.queryByRole( 'img', { name: 'july-9' } )
399
+ ).not.toBeInTheDocument();
400
+
401
+ await click( currentSelectedItem );
402
+
403
+ // expect that the other image is only visible after opening popover with options
404
+ expect( screen.getByRole( 'img', { name: 'july-9' } ) ).toBeVisible();
405
+ expect(
406
+ screen.getByRole( 'option', { name: 'july-9' } )
407
+ ).toBeVisible();
878
408
  } );
879
409
  } );