@wordpress/components 26.0.1 → 27.0.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 (223) hide show
  1. package/.stylelintrc.js +2 -2
  2. package/CHANGELOG.md +23 -0
  3. package/CONTRIBUTING.md +72 -0
  4. package/build/autocomplete/index.js +3 -8
  5. package/build/autocomplete/index.js.map +1 -1
  6. package/build/color-picker/component.js +10 -1
  7. package/build/color-picker/component.js.map +1 -1
  8. package/build/color-picker/styles.js +8 -9
  9. package/build/color-picker/styles.js.map +1 -1
  10. package/build/combobox-control/index.js +4 -9
  11. package/build/combobox-control/index.js.map +1 -1
  12. package/build/custom-select-control/index.js +2 -15
  13. package/build/custom-select-control/index.js.map +1 -1
  14. package/build/custom-select-control-v2/custom-select-item.js +32 -0
  15. package/build/custom-select-control-v2/custom-select-item.js.map +1 -0
  16. package/build/custom-select-control-v2/custom-select.js +91 -0
  17. package/build/custom-select-control-v2/custom-select.js.map +1 -0
  18. package/build/custom-select-control-v2/default-component/index.js +41 -0
  19. package/build/custom-select-control-v2/default-component/index.js.map +1 -0
  20. package/build/custom-select-control-v2/index.js +13 -82
  21. package/build/custom-select-control-v2/index.js.map +1 -1
  22. package/build/custom-select-control-v2/legacy-adapter.js +29 -0
  23. package/build/custom-select-control-v2/legacy-adapter.js.map +1 -0
  24. package/build/custom-select-control-v2/legacy-component/index.js +123 -0
  25. package/build/custom-select-control-v2/legacy-component/index.js.map +1 -0
  26. package/build/custom-select-control-v2/styles.js +73 -50
  27. package/build/custom-select-control-v2/styles.js.map +1 -1
  28. package/build/custom-select-control-v2/types.js.map +1 -1
  29. package/build/font-size-picker/font-size-picker-select.js +0 -1
  30. package/build/font-size-picker/font-size-picker-select.js.map +1 -1
  31. package/build/form-token-field/index.js +3 -8
  32. package/build/form-token-field/index.js.map +1 -1
  33. package/build/form-token-field/suggestions-list.js +5 -12
  34. package/build/form-token-field/suggestions-list.js.map +1 -1
  35. package/build/mobile/keyboard-aware-flat-list/use-scroll-to-section.native.js +1 -1
  36. package/build/mobile/keyboard-aware-flat-list/use-scroll-to-section.native.js.map +1 -1
  37. package/build/modal/index.js +2 -10
  38. package/build/modal/index.js.map +1 -1
  39. package/build/progress-bar/styles.js +5 -5
  40. package/build/progress-bar/styles.js.map +1 -1
  41. package/build/utils/with-ignore-ime-events.js +34 -0
  42. package/build/utils/with-ignore-ime-events.js.map +1 -0
  43. package/build-module/autocomplete/index.js +3 -8
  44. package/build-module/autocomplete/index.js.map +1 -1
  45. package/build-module/color-picker/component.js +11 -2
  46. package/build-module/color-picker/component.js.map +1 -1
  47. package/build-module/color-picker/styles.js +8 -9
  48. package/build-module/color-picker/styles.js.map +1 -1
  49. package/build-module/combobox-control/index.js +4 -9
  50. package/build-module/combobox-control/index.js.map +1 -1
  51. package/build-module/custom-select-control/index.js +2 -15
  52. package/build-module/custom-select-control/index.js.map +1 -1
  53. package/build-module/custom-select-control-v2/custom-select-item.js +26 -0
  54. package/build-module/custom-select-control-v2/custom-select-item.js.map +1 -0
  55. package/build-module/custom-select-control-v2/custom-select.js +82 -0
  56. package/build-module/custom-select-control-v2/custom-select.js.map +1 -0
  57. package/build-module/custom-select-control-v2/default-component/index.js +30 -0
  58. package/build-module/custom-select-control-v2/default-component/index.js.map +1 -0
  59. package/build-module/custom-select-control-v2/index.js +2 -74
  60. package/build-module/custom-select-control-v2/index.js.map +1 -1
  61. package/build-module/custom-select-control-v2/legacy-adapter.js +21 -0
  62. package/build-module/custom-select-control-v2/legacy-adapter.js.map +1 -0
  63. package/build-module/custom-select-control-v2/legacy-component/index.js +111 -0
  64. package/build-module/custom-select-control-v2/legacy-component/index.js.map +1 -0
  65. package/build-module/custom-select-control-v2/styles.js +73 -42
  66. package/build-module/custom-select-control-v2/styles.js.map +1 -1
  67. package/build-module/custom-select-control-v2/types.js.map +1 -1
  68. package/build-module/font-size-picker/font-size-picker-select.js +0 -1
  69. package/build-module/font-size-picker/font-size-picker-select.js.map +1 -1
  70. package/build-module/form-token-field/index.js +3 -8
  71. package/build-module/form-token-field/index.js.map +1 -1
  72. package/build-module/form-token-field/suggestions-list.js +5 -12
  73. package/build-module/form-token-field/suggestions-list.js.map +1 -1
  74. package/build-module/mobile/keyboard-aware-flat-list/use-scroll-to-section.native.js +1 -1
  75. package/build-module/mobile/keyboard-aware-flat-list/use-scroll-to-section.native.js.map +1 -1
  76. package/build-module/modal/index.js +3 -10
  77. package/build-module/modal/index.js.map +1 -1
  78. package/build-module/progress-bar/styles.js +5 -5
  79. package/build-module/progress-bar/styles.js.map +1 -1
  80. package/build-module/utils/with-ignore-ime-events.js +28 -0
  81. package/build-module/utils/with-ignore-ime-events.js.map +1 -0
  82. package/build-style/style-rtl.css +8 -1
  83. package/build-style/style.css +8 -1
  84. package/build-types/alignment-matrix-control/stories/index.story.d.ts +1 -1
  85. package/build-types/angle-picker-control/stories/index.story.d.ts +1 -1
  86. package/build-types/animate/stories/index.story.d.ts +7 -7
  87. package/build-types/autocomplete/index.d.ts.map +1 -1
  88. package/build-types/base-control/stories/index.story.d.ts +1 -1
  89. package/build-types/border-box-control/stories/index.story.d.ts +1 -1
  90. package/build-types/border-control/stories/index.story.d.ts +6 -6
  91. package/build-types/box-control/stories/index.story.d.ts +6 -6
  92. package/build-types/button/stories/e2e/index.story.d.ts +1 -1
  93. package/build-types/button/stories/index.story.d.ts +7 -7
  94. package/build-types/card/stories/index.story.d.ts +2 -2
  95. package/build-types/circular-option-picker/stories/index.story.d.ts +5 -5
  96. package/build-types/color-palette/stories/index.story.d.ts +3 -3
  97. package/build-types/color-picker/component.d.ts.map +1 -1
  98. package/build-types/color-picker/stories/index.story.d.ts +1 -1
  99. package/build-types/color-picker/styles.d.ts.map +1 -1
  100. package/build-types/combobox-control/index.d.ts.map +1 -1
  101. package/build-types/combobox-control/stories/index.story.d.ts +2 -2
  102. package/build-types/confirm-dialog/stories/index.story.d.ts +2 -2
  103. package/build-types/custom-gradient-picker/stories/index.story.d.ts +1 -1
  104. package/build-types/custom-select-control/index.d.ts.map +1 -1
  105. package/build-types/custom-select-control-v2/custom-select-item.d.ts +9 -0
  106. package/build-types/custom-select-control-v2/custom-select-item.d.ts.map +1 -0
  107. package/build-types/custom-select-control-v2/custom-select.d.ts +6 -0
  108. package/build-types/custom-select-control-v2/custom-select.d.ts.map +1 -0
  109. package/build-types/custom-select-control-v2/default-component/index.d.ts +5 -0
  110. package/build-types/custom-select-control-v2/default-component/index.d.ts.map +1 -0
  111. package/build-types/custom-select-control-v2/index.d.ts +5 -6
  112. package/build-types/custom-select-control-v2/index.d.ts.map +1 -1
  113. package/build-types/custom-select-control-v2/legacy-adapter.d.ts +6 -0
  114. package/build-types/custom-select-control-v2/legacy-adapter.d.ts.map +1 -0
  115. package/build-types/custom-select-control-v2/legacy-component/index.d.ts +5 -0
  116. package/build-types/custom-select-control-v2/legacy-component/index.d.ts.map +1 -0
  117. package/build-types/custom-select-control-v2/stories/default.story.d.ts +29 -0
  118. package/build-types/custom-select-control-v2/stories/default.story.d.ts.map +1 -0
  119. package/build-types/custom-select-control-v2/stories/legacy.story.d.ts +12 -0
  120. package/build-types/custom-select-control-v2/stories/legacy.story.d.ts.map +1 -0
  121. package/build-types/custom-select-control-v2/styles.d.ts +31 -6
  122. package/build-types/custom-select-control-v2/styles.d.ts.map +1 -1
  123. package/build-types/custom-select-control-v2/types.d.ts +137 -14
  124. package/build-types/custom-select-control-v2/types.d.ts.map +1 -1
  125. package/build-types/dimension-control/stories/index.story.d.ts +2 -2
  126. package/build-types/drop-zone/stories/index.story.d.ts +1 -1
  127. package/build-types/dropdown/stories/index.story.d.ts +3 -3
  128. package/build-types/dropdown-menu/stories/index.story.d.ts +2 -2
  129. package/build-types/dropdown-menu-v2/stories/index.story.d.ts.map +1 -1
  130. package/build-types/duotone-picker/stories/duotone-picker.story.d.ts +1 -1
  131. package/build-types/duotone-picker/stories/duotone-swatch.story.d.ts +3 -3
  132. package/build-types/focal-point-picker/stories/index.story.d.ts +4 -4
  133. package/build-types/font-size-picker/font-size-picker-select.d.ts.map +1 -1
  134. package/build-types/form-file-upload/stories/index.story.d.ts +5 -5
  135. package/build-types/form-token-field/index.d.ts.map +1 -1
  136. package/build-types/form-token-field/suggestions-list.d.ts.map +1 -1
  137. package/build-types/gradient-picker/stories/index.story.d.ts +3 -3
  138. package/build-types/guide/stories/index.story.d.ts +1 -1
  139. package/build-types/icon/stories/index.story.d.ts +4 -4
  140. package/build-types/input-control/stories/index.story.d.ts +6 -6
  141. package/build-types/keyboard-shortcuts/stories/index.story.d.ts +1 -1
  142. package/build-types/menu-group/stories/index.story.d.ts +1 -1
  143. package/build-types/menu-item/stories/index.story.d.ts +4 -4
  144. package/build-types/modal/index.d.ts.map +1 -1
  145. package/build-types/navigation/stories/index.story.d.ts +6 -6
  146. package/build-types/notice/stories/index.story.d.ts +4 -4
  147. package/build-types/number-control/stories/index.story.d.ts +1 -1
  148. package/build-types/palette-edit/stories/index.story.d.ts +2 -2
  149. package/build-types/progress-bar/stories/index.story.d.ts.map +1 -1
  150. package/build-types/query-controls/stories/index.story.d.ts +1 -1
  151. package/build-types/resizable-box/stories/index.story.d.ts +2 -2
  152. package/build-types/responsive-wrapper/stories/index.story.d.ts +1 -1
  153. package/build-types/sandbox/stories/index.story.d.ts +1 -1
  154. package/build-types/search-control/stories/index.story.d.ts +2 -2
  155. package/build-types/select-control/stories/index.story.d.ts +2 -2
  156. package/build-types/shortcut/stories/index.story.d.ts +1 -1
  157. package/build-types/tab-panel/stories/index.story.d.ts +4 -4
  158. package/build-types/tabs/stories/index.story.d.ts +9 -9
  159. package/build-types/tabs/stories/index.story.d.ts.map +1 -1
  160. package/build-types/text/stories/index.story.d.ts +3 -3
  161. package/build-types/theme/stories/index.story.d.ts +1 -1
  162. package/build-types/theme/stories/index.story.d.ts.map +1 -1
  163. package/build-types/toggle-control/stories/index.story.d.ts +2 -2
  164. package/build-types/toolbar/stories/index.story.d.ts +3 -3
  165. package/build-types/tooltip/stories/index.story.d.ts +1 -1
  166. package/build-types/tree-grid/stories/index.story.d.ts +1 -1
  167. package/build-types/tree-select/stories/index.story.d.ts +1 -1
  168. package/build-types/utils/with-ignore-ime-events.d.ts +15 -0
  169. package/build-types/utils/with-ignore-ime-events.d.ts.map +1 -0
  170. package/build-types/v-stack/stories/index.story.d.ts +1 -1
  171. package/package.json +19 -20
  172. package/src/alignment-matrix-control/test/index.tsx +3 -1
  173. package/src/autocomplete/index.tsx +3 -10
  174. package/src/circular-option-picker/test/index.tsx +4 -1
  175. package/src/color-picker/component.tsx +22 -11
  176. package/src/color-picker/styles.ts +1 -15
  177. package/src/combobox-control/index.tsx +33 -41
  178. package/src/composite/legacy/test/index.tsx +18 -2
  179. package/src/custom-select-control/README.md +0 -10
  180. package/src/custom-select-control/index.js +3 -22
  181. package/src/custom-select-control/stories/index.story.js +0 -1
  182. package/src/custom-select-control/test/index.js +17 -17
  183. package/src/custom-select-control-v2/README.md +97 -7
  184. package/src/custom-select-control-v2/custom-select-item.tsx +29 -0
  185. package/src/custom-select-control-v2/custom-select.tsx +122 -0
  186. package/src/custom-select-control-v2/default-component/index.tsx +24 -0
  187. package/src/custom-select-control-v2/index.tsx +2 -102
  188. package/src/custom-select-control-v2/legacy-adapter.tsx +25 -0
  189. package/src/custom-select-control-v2/legacy-component/index.tsx +133 -0
  190. package/src/custom-select-control-v2/stories/{index.story.tsx → default.story.tsx} +27 -33
  191. package/src/custom-select-control-v2/stories/legacy.story.tsx +88 -0
  192. package/src/custom-select-control-v2/styles.ts +82 -38
  193. package/src/custom-select-control-v2/test/index.tsx +808 -148
  194. package/src/custom-select-control-v2/types.ts +148 -20
  195. package/src/dropdown-menu-v2/stories/index.story.tsx +1 -0
  196. package/src/dropdown-menu-v2/test/index.tsx +4 -1
  197. package/src/font-size-picker/font-size-picker-select.tsx +0 -1
  198. package/src/form-token-field/index.tsx +3 -10
  199. package/src/form-token-field/suggestions-list.tsx +5 -17
  200. package/src/mobile/keyboard-aware-flat-list/use-scroll-to-section.native.js +1 -1
  201. package/src/modal/index.tsx +2 -12
  202. package/src/modal/style.scss +1 -0
  203. package/src/progress-bar/stories/index.story.tsx +1 -0
  204. package/src/progress-bar/styles.ts +2 -2
  205. package/src/tab-panel/test/index.tsx +8 -1
  206. package/src/tabs/stories/index.story.tsx +1 -0
  207. package/src/tabs/test/index.tsx +36 -6
  208. package/src/theme/stories/index.story.tsx +1 -0
  209. package/src/toggle-group-control/test/index.tsx +4 -0
  210. package/src/toolbar/toolbar-group/style.scss +1 -0
  211. package/src/tooltip/test/index.tsx +5 -0
  212. package/src/utils/with-ignore-ime-events.ts +32 -0
  213. package/tsconfig.json +0 -1
  214. package/tsconfig.tsbuildinfo +1 -1
  215. package/build/custom-select-control/styles.js +0 -27
  216. package/build/custom-select-control/styles.js.map +0 -1
  217. package/build-module/custom-select-control/styles.js +0 -18
  218. package/build-module/custom-select-control/styles.js.map +0 -1
  219. package/build-types/custom-select-control/styles.d.ts +0 -11
  220. package/build-types/custom-select-control/styles.d.ts.map +0 -1
  221. package/build-types/custom-select-control-v2/stories/index.story.d.ts +0 -20
  222. package/build-types/custom-select-control-v2/stories/index.story.d.ts.map +0 -1
  223. package/src/custom-select-control/styles.ts +0 -28
@@ -16,7 +16,11 @@ import { __ } from '@wordpress/i18n';
16
16
  /**
17
17
  * Internal dependencies
18
18
  */
19
- import { useContextSystem, contextConnect } from '../context';
19
+ import {
20
+ useContextSystem,
21
+ contextConnect,
22
+ ContextSystemProvider,
23
+ } from '../context';
20
24
  import {
21
25
  ColorfulWrapper,
22
26
  SelectControl,
@@ -39,6 +43,9 @@ const options = [
39
43
  { label: 'Hex', value: 'hex' as const },
40
44
  ];
41
45
 
46
+ // `isBorderless` is still experimental and not a public prop for InputControl yet.
47
+ const BORDERLESS_SELECT_CONTROL_CONTEXT = { InputBase: { isBorderless: true } };
48
+
42
49
  const UnconnectedColorPicker = (
43
50
  props: ColorPickerProps,
44
51
  forwardedRef: ForwardedRef< any >
@@ -107,16 +114,20 @@ const UnconnectedColorPicker = (
107
114
  />
108
115
  <AuxiliaryColorArtefactWrapper>
109
116
  <AuxiliaryColorArtefactHStackHeader justify="space-between">
110
- <SelectControl
111
- __nextHasNoMarginBottom
112
- options={ options }
113
- value={ colorType }
114
- onChange={ ( nextColorType ) =>
115
- setColorType( nextColorType as ColorType )
116
- }
117
- label={ __( 'Color format' ) }
118
- hideLabelFromVision
119
- />
117
+ <ContextSystemProvider
118
+ value={ BORDERLESS_SELECT_CONTROL_CONTEXT }
119
+ >
120
+ <SelectControl
121
+ __nextHasNoMarginBottom
122
+ options={ options }
123
+ value={ colorType }
124
+ onChange={ ( nextColorType ) =>
125
+ setColorType( nextColorType as ColorType )
126
+ }
127
+ label={ __( 'Color format' ) }
128
+ hideLabelFromVision
129
+ />
130
+ </ContextSystemProvider>
120
131
  <ColorCopyButton
121
132
  color={ safeColordColor }
122
133
  colorType={ copyFormat || colorType }
@@ -14,29 +14,15 @@ import { boxSizingReset } from '../utils';
14
14
  import Button from '../button';
15
15
  import { Flex } from '../flex';
16
16
  import { HStack } from '../h-stack';
17
- import {
18
- BackdropUI,
19
- Container as InputControlContainer,
20
- } from '../input-control/styles/input-control-styles';
21
17
  import CONFIG from '../utils/config-values';
22
18
 
23
19
  export const NumberControlWrapper = styled( NumberControl )`
24
- ${ InputControlContainer } {
25
- width: ${ space( 24 ) };
26
- }
20
+ width: ${ space( 24 ) };
27
21
  `;
28
22
 
29
23
  export const SelectControl = styled( InnerSelectControl )`
30
24
  margin-left: ${ space( -2 ) };
31
25
  width: 5em;
32
- /*
33
- * Remove border, but preserve focus styles
34
- * TODO: this override should be removed,
35
- * see https://github.com/WordPress/gutenberg/pull/50609
36
- */
37
- select:not( :focus ) ~ ${ BackdropUI }${ BackdropUI }${ BackdropUI } {
38
- border-color: transparent;
39
- }
40
26
  `;
41
27
 
42
28
  export const RangeControl = styled( InnerRangeControl )`
@@ -33,6 +33,7 @@ import { normalizeTextString } from '../utils/strings';
33
33
  import type { ComboboxControlOption, ComboboxControlProps } from './types';
34
34
  import type { TokenInputProps } from '../form-token-field/types';
35
35
  import { useDeprecated36pxDefaultSizeProp } from '../utils/use-deprecated-props';
36
+ import { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';
36
37
 
37
38
  const noop = () => {};
38
39
 
@@ -186,51 +187,42 @@ function ComboboxControl( props: ComboboxControlProps ) {
186
187
  setIsExpanded( true );
187
188
  };
188
189
 
189
- const onKeyDown: React.KeyboardEventHandler< HTMLDivElement > = (
190
- event
191
- ) => {
192
- let preventDefault = false;
190
+ const onKeyDown: React.KeyboardEventHandler< HTMLDivElement > =
191
+ withIgnoreIMEEvents( ( event ) => {
192
+ let preventDefault = false;
193
193
 
194
- if (
195
- event.defaultPrevented ||
196
- // Ignore keydowns from IMEs
197
- event.nativeEvent.isComposing ||
198
- // Workaround for Mac Safari where the final Enter/Backspace of an IME composition
199
- // is `isComposing=false`, even though it's technically still part of the composition.
200
- // These can only be detected by keyCode.
201
- event.keyCode === 229
202
- ) {
203
- return;
204
- }
194
+ if ( event.defaultPrevented ) {
195
+ return;
196
+ }
205
197
 
206
- switch ( event.code ) {
207
- case 'Enter':
208
- if ( selectedSuggestion ) {
209
- onSuggestionSelected( selectedSuggestion );
198
+ switch ( event.code ) {
199
+ case 'Enter':
200
+ if ( selectedSuggestion ) {
201
+ onSuggestionSelected( selectedSuggestion );
202
+ preventDefault = true;
203
+ }
204
+ break;
205
+ case 'ArrowUp':
206
+ handleArrowNavigation( -1 );
210
207
  preventDefault = true;
211
- }
212
- break;
213
- case 'ArrowUp':
214
- handleArrowNavigation( -1 );
215
- preventDefault = true;
216
- break;
217
- case 'ArrowDown':
218
- handleArrowNavigation( 1 );
219
- preventDefault = true;
220
- break;
221
- case 'Escape':
222
- setIsExpanded( false );
223
- setSelectedSuggestion( null );
224
- preventDefault = true;
225
- break;
226
- default:
227
- break;
228
- }
208
+ break;
209
+ case 'ArrowDown':
210
+ handleArrowNavigation( 1 );
211
+ preventDefault = true;
212
+ break;
213
+ case 'Escape':
214
+ setIsExpanded( false );
215
+ setSelectedSuggestion( null );
216
+ preventDefault = true;
217
+ break;
218
+ default:
219
+ break;
220
+ }
229
221
 
230
- if ( preventDefault ) {
231
- event.preventDefault();
232
- }
233
- };
222
+ if ( preventDefault ) {
223
+ event.preventDefault();
224
+ }
225
+ } );
234
226
 
235
227
  const onBlur = () => {
236
228
  setInputHasFocus( false );
@@ -2,7 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { queryByAttribute, render, screen } from '@testing-library/react';
5
- import { press, waitFor } from '@ariakit/test';
5
+ import { press, sleep, waitFor } from '@ariakit/test';
6
6
 
7
7
  /**
8
8
  * Internal dependencies
@@ -178,10 +178,13 @@ describe.each( [
178
178
  );
179
179
  renderAndValidate( <Test /> );
180
180
 
181
+ await sleep();
181
182
  await press.Tab();
182
183
  expect( screen.getByText( 'Before' ) ).toHaveFocus();
184
+ await sleep();
183
185
  await press.Tab();
184
186
  expect( screen.getByText( 'Item 1' ) ).toHaveFocus();
187
+ await sleep();
185
188
  await press.Tab();
186
189
  expect( screen.getByText( 'After' ) ).toHaveFocus();
187
190
  await press.ShiftTab();
@@ -210,6 +213,7 @@ describe.each( [
210
213
 
211
214
  expect( item2 ).toBeDisabled();
212
215
 
216
+ await sleep();
213
217
  await press.Tab();
214
218
  expect( item1 ).toHaveFocus();
215
219
  await press.ArrowDown();
@@ -239,6 +243,7 @@ describe.each( [
239
243
  expect( item2 ).toBeEnabled();
240
244
  expect( item2 ).toHaveAttribute( 'aria-disabled', 'true' );
241
245
 
246
+ await sleep();
242
247
  await press.Tab();
243
248
  expect( item1 ).toHaveFocus();
244
249
  await press.ArrowDown();
@@ -274,8 +279,9 @@ describe.each( [
274
279
  renderAndValidate( <Test /> );
275
280
  const { item2 } = getOneDimensionalItems();
276
281
 
282
+ await sleep();
277
283
  await press.Tab();
278
- expect( item2 ).toHaveFocus();
284
+ await waitFor( () => expect( item2 ).toHaveFocus() );
279
285
  } );
280
286
  } );
281
287
 
@@ -317,6 +323,7 @@ describe.each( [
317
323
  test( 'All directions work with no orientation', async () => {
318
324
  const { item1, item2, item3 } = useOneDimensionalTest();
319
325
 
326
+ await sleep();
320
327
  await press.Tab();
321
328
  expect( item1 ).toHaveFocus();
322
329
  await press.ArrowDown();
@@ -354,6 +361,7 @@ describe.each( [
354
361
  orientation: 'horizontal',
355
362
  } );
356
363
 
364
+ await sleep();
357
365
  await press.Tab();
358
366
  expect( item1 ).toHaveFocus();
359
367
  await press.ArrowDown();
@@ -383,6 +391,7 @@ describe.each( [
383
391
  orientation: 'vertical',
384
392
  } );
385
393
 
394
+ await sleep();
386
395
  await press.Tab();
387
396
  expect( item1 ).toHaveFocus();
388
397
  await press( next );
@@ -412,6 +421,7 @@ describe.each( [
412
421
  loop: true,
413
422
  } );
414
423
 
424
+ await sleep();
415
425
  await press.Tab();
416
426
  expect( item1 ).toHaveFocus();
417
427
  await press.ArrowDown();
@@ -434,6 +444,7 @@ describe.each( [
434
444
  const { itemA1, itemA2, itemA3, itemB1, itemB2, itemC1, itemC3 } =
435
445
  useTwoDimensionalTest();
436
446
 
447
+ await sleep();
437
448
  await press.Tab();
438
449
  expect( itemA1 ).toHaveFocus();
439
450
  await press.ArrowUp();
@@ -470,6 +481,7 @@ describe.each( [
470
481
  const { itemA1, itemA2, itemA3, itemB1, itemC1, itemC3 } =
471
482
  useTwoDimensionalTest( { loop: true } );
472
483
 
484
+ await sleep();
473
485
  await press.Tab();
474
486
  expect( itemA1 ).toHaveFocus();
475
487
  await press( next );
@@ -494,6 +506,7 @@ describe.each( [
494
506
  const { itemA1, itemA2, itemA3, itemB1, itemC1, itemC3 } =
495
507
  useTwoDimensionalTest( { wrap: true } );
496
508
 
509
+ await sleep();
497
510
  await press.Tab();
498
511
  expect( itemA1 ).toHaveFocus();
499
512
  await press( next );
@@ -526,6 +539,7 @@ describe.each( [
526
539
  wrap: true,
527
540
  } );
528
541
 
542
+ await sleep();
529
543
  await press.Tab();
530
544
  expect( itemA1 ).toHaveFocus();
531
545
  await press( previous );
@@ -541,6 +555,7 @@ describe.each( [
541
555
  test( 'Focus shifts if vertical neighbour unavailable when shift enabled', async () => {
542
556
  const { itemA1, itemB1, itemB2, itemC1 } = useShiftTest( true );
543
557
 
558
+ await sleep();
544
559
  await press.Tab();
545
560
  expect( itemA1 ).toHaveFocus();
546
561
  await press.ArrowDown();
@@ -562,6 +577,7 @@ describe.each( [
562
577
  test( 'Focus does not shift if vertical neighbour unavailable when shift not enabled', async () => {
563
578
  const { itemA1, itemB1, itemB2 } = useShiftTest( false );
564
579
 
580
+ await sleep();
565
581
  await press.Tab();
566
582
  expect( itemA1 ).toHaveFocus();
567
583
  await press.ArrowDown();
@@ -41,7 +41,6 @@ function MyCustomSelectControl() {
41
41
  const [ , setFontSize ] = useState();
42
42
  return (
43
43
  <CustomSelectControl
44
- __nextUnconstrainedWidth
45
44
  label="Font Size"
46
45
  options={ options }
47
46
  onChange={ ( { selectedItem } ) => setFontSize( selectedItem ) }
@@ -53,7 +52,6 @@ function MyControlledCustomSelectControl() {
53
52
  const [ fontSize, setFontSize ] = useState( options[ 0 ] );
54
53
  return (
55
54
  <CustomSelectControl
56
- __nextUnconstrainedWidth
57
55
  label="Font Size"
58
56
  options={ options }
59
57
  onChange={ ( { selectedItem } ) => setFontSize( selectedItem ) }
@@ -114,14 +112,6 @@ Can be used to externally control the value of the control, like in the `MyContr
114
112
  - Type: `Object`
115
113
  - Required: No
116
114
 
117
- #### __nextUnconstrainedWidth
118
-
119
- Start opting into the unconstrained width style that will become the default in a future version, currently scheduled to be WordPress 6.4. (The prop can be safely removed once this happens.)
120
-
121
- - Type: `Boolean`
122
- - Required: No
123
- - Default: `false`
124
-
125
115
  #### onMouseOver
126
116
 
127
117
  A handler for onMouseOver events.
@@ -11,7 +11,6 @@ import classnames from 'classnames';
11
11
  import { Icon, check } from '@wordpress/icons';
12
12
  import { __, sprintf } from '@wordpress/i18n';
13
13
  import { useCallback, useState } from '@wordpress/element';
14
- import deprecated from '@wordpress/deprecated';
15
14
 
16
15
  /**
17
16
  * Internal dependencies
@@ -19,9 +18,9 @@ import deprecated from '@wordpress/deprecated';
19
18
  import { VisuallyHidden } from '../visually-hidden';
20
19
  import { Select as SelectControlSelect } from '../select-control/styles/select-control-styles';
21
20
  import SelectControlChevronDown from '../select-control/chevron-down';
22
- import { InputBaseWithBackCompatMinWidth } from './styles';
23
21
  import { StyledLabel } from '../base-control/styles/base-control-styles';
24
22
  import { useDeprecated36pxDefaultSizeProp } from '../utils/use-deprecated-props';
23
+ import InputBase from '../input-control/input-base';
25
24
 
26
25
  const itemToString = ( item ) => item?.name;
27
26
  // This is needed so that in Windows, where
@@ -67,8 +66,6 @@ export default function CustomSelectControl( props ) {
67
66
  const {
68
67
  /** Start opting into the larger default height that will become the default size in a future version. */
69
68
  __next40pxDefaultSize = false,
70
- /** Start opting into the unconstrained width that will become the default in a future version. */
71
- __nextUnconstrainedWidth = false,
72
69
  className,
73
70
  hideLabelFromVision,
74
71
  label,
@@ -116,17 +113,6 @@ export default function CustomSelectControl( props ) {
116
113
  onBlur?.( e );
117
114
  }
118
115
 
119
- if ( ! __nextUnconstrainedWidth ) {
120
- deprecated(
121
- 'Constrained width styles for wp.components.CustomSelectControl',
122
- {
123
- since: '6.1',
124
- version: '6.4',
125
- hint: 'Set the `__nextUnconstrainedWidth` prop to true to start opting into the new styles, which will become the default in a future version',
126
- }
127
- );
128
- }
129
-
130
116
  function getDescribedBy() {
131
117
  if ( describedBy ) {
132
118
  return describedBy;
@@ -180,14 +166,9 @@ export default function CustomSelectControl( props ) {
180
166
  { label }
181
167
  </StyledLabel>
182
168
  ) }
183
- <InputBaseWithBackCompatMinWidth
169
+ <InputBase
184
170
  __next40pxDefaultSize={ __next40pxDefaultSize }
185
- __nextUnconstrainedWidth={ __nextUnconstrainedWidth }
186
171
  isFocused={ isOpen || isFocused }
187
- __unstableInputWidth={
188
- __nextUnconstrainedWidth ? undefined : 'auto'
189
- }
190
- labelPosition={ __nextUnconstrainedWidth ? undefined : 'top' }
191
172
  size={ size }
192
173
  suffix={ <SelectControlChevronDown /> }
193
174
  >
@@ -215,7 +196,7 @@ export default function CustomSelectControl( props ) {
215
196
  </span>
216
197
  ) }
217
198
  </SelectControlSelect>
218
- </InputBaseWithBackCompatMinWidth>
199
+ </InputBase>
219
200
  { /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */ }
220
201
  <ul { ...menuProps } onKeyDown={ onKeyDownHandler }>
221
202
  { isOpen &&
@@ -20,7 +20,6 @@ export default {
20
20
 
21
21
  export const Default = CustomSelectControl.bind( {} );
22
22
  Default.args = {
23
- __nextUnconstrainedWidth: true,
24
23
  label: 'Label',
25
24
  options: [
26
25
  {
@@ -12,7 +12,7 @@ import { useState } from '@wordpress/element';
12
12
  /**
13
13
  * Internal dependencies
14
14
  */
15
- import CustomSelectControl from '..';
15
+ import UncontrolledCustomSelectControl from '..';
16
16
 
17
17
  const customClass = 'amber-skies';
18
18
 
@@ -46,14 +46,14 @@ const props = {
46
46
  },
47
47
  },
48
48
  ],
49
- __nextUnconstrainedWidth: true,
50
49
  };
51
50
 
52
- const ControlledCustomSelectControl = ( { options } ) => {
51
+ const ControlledCustomSelectControl = ( { options, ...restProps } ) => {
53
52
  const [ value, setValue ] = useState( options[ 0 ] );
54
53
  return (
55
- <CustomSelectControl
56
- { ...props }
54
+ <UncontrolledCustomSelectControl
55
+ { ...restProps }
56
+ options={ options }
57
57
  onChange={ ( { selectedItem } ) => setValue( selectedItem ) }
58
58
  value={ options.find( ( option ) => option.key === value.key ) }
59
59
  />
@@ -61,7 +61,7 @@ const ControlledCustomSelectControl = ( { options } ) => {
61
61
  };
62
62
 
63
63
  describe.each( [
64
- [ 'uncontrolled', CustomSelectControl ],
64
+ [ 'uncontrolled', UncontrolledCustomSelectControl ],
65
65
  [ 'controlled', ControlledCustomSelectControl ],
66
66
  ] )( 'CustomSelectControl %s', ( ...modeAndComponent ) => {
67
67
  const [ , Component ] = modeAndComponent;
@@ -99,7 +99,7 @@ describe.each( [
99
99
  it( 'Should keep current selection if dropdown is closed without changing selection', async () => {
100
100
  const user = userEvent.setup();
101
101
 
102
- render( <CustomSelectControl { ...props } /> );
102
+ render( <Component { ...props } /> );
103
103
 
104
104
  const currentSelectedItem = screen.getByRole( 'button', {
105
105
  expanded: false,
@@ -128,7 +128,7 @@ describe.each( [
128
128
  it( 'Should apply class only to options that have a className defined', async () => {
129
129
  const user = userEvent.setup();
130
130
 
131
- render( <CustomSelectControl { ...props } /> );
131
+ render( <Component { ...props } /> );
132
132
 
133
133
  await user.click(
134
134
  screen.getByRole( 'button', {
@@ -166,7 +166,7 @@ describe.each( [
166
166
  const customStyles =
167
167
  'background-color: rgb(127, 255, 212); rotate: 13deg;';
168
168
 
169
- render( <CustomSelectControl { ...props } /> );
169
+ render( <Component { ...props } /> );
170
170
 
171
171
  await user.click(
172
172
  screen.getByRole( 'button', {
@@ -201,7 +201,7 @@ describe.each( [
201
201
 
202
202
  it( 'does not show selected hint by default', () => {
203
203
  render(
204
- <CustomSelectControl
204
+ <Component
205
205
  { ...props }
206
206
  label="Custom select"
207
207
  options={ [
@@ -220,7 +220,7 @@ describe.each( [
220
220
 
221
221
  it( 'shows selected hint when __experimentalShowSelectedHint is set', () => {
222
222
  render(
223
- <CustomSelectControl
223
+ <Component
224
224
  { ...props }
225
225
  label="Custom select"
226
226
  options={ [
@@ -249,7 +249,7 @@ describe.each( [
249
249
  role="none"
250
250
  onKeyDown={ onKeyDown }
251
251
  >
252
- <CustomSelectControl { ...props } />
252
+ <Component { ...props } />
253
253
  </div>
254
254
  );
255
255
  const currentSelectedItem = screen.getByRole( 'button', {
@@ -268,7 +268,7 @@ describe.each( [
268
268
  it( 'Should be able to change selection using keyboard', async () => {
269
269
  const user = userEvent.setup();
270
270
 
271
- render( <CustomSelectControl { ...props } /> );
271
+ render( <Component { ...props } /> );
272
272
 
273
273
  const currentSelectedItem = screen.getByRole( 'button', {
274
274
  expanded: false,
@@ -293,7 +293,7 @@ describe.each( [
293
293
  it( 'Should be able to type characters to select matching options', async () => {
294
294
  const user = userEvent.setup();
295
295
 
296
- render( <CustomSelectControl { ...props } /> );
296
+ render( <Component { ...props } /> );
297
297
 
298
298
  const currentSelectedItem = screen.getByRole( 'button', {
299
299
  expanded: false,
@@ -315,7 +315,7 @@ describe.each( [
315
315
  it( 'Can change selection with a focused input and closed dropdown if typed characters match an option', async () => {
316
316
  const user = userEvent.setup();
317
317
 
318
- render( <CustomSelectControl { ...props } /> );
318
+ render( <Component { ...props } /> );
319
319
 
320
320
  const currentSelectedItem = screen.getByRole( 'button', {
321
321
  expanded: false,
@@ -341,7 +341,7 @@ describe.each( [
341
341
  it( 'Should have correct aria-selected value for selections', async () => {
342
342
  const user = userEvent.setup();
343
343
 
344
- render( <CustomSelectControl { ...props } /> );
344
+ render( <Component { ...props } /> );
345
345
 
346
346
  const currentSelectedItem = screen.getByRole( 'button', {
347
347
  expanded: false,
@@ -398,7 +398,7 @@ describe.each( [
398
398
  const onBlurMock = jest.fn();
399
399
 
400
400
  render(
401
- <CustomSelectControl
401
+ <Component
402
402
  { ...props }
403
403
  onFocus={ onFocusMock }
404
404
  onBlur={ onBlurMock }
@@ -1,11 +1,93 @@
1
+ # CustomSelect
2
+
1
3
  <div class="callout callout-alert">
2
4
  This feature is still experimental. “Experimental” means this is an early implementation subject to drastic and breaking changes.
3
5
  </div>
4
6
 
5
- ### `CustomSelect`
6
-
7
7
  Used to render a customizable select control component.
8
8
 
9
+ ## Development guidelines
10
+
11
+ ### Usage
12
+
13
+ #### Uncontrolled Mode
14
+
15
+ CustomSelect can be used in an uncontrolled mode, where the component manages its own state. In this mode, the `defaultValue` prop can be used to set the initial selected value. If this prop is not set, the first value from the children will be selected by default.
16
+
17
+ ```jsx
18
+ const UncontrolledCustomSelect = () => (
19
+ <CustomSelect label="Colors">
20
+ <CustomSelectItem value="Blue">
21
+ { /* The `defaultValue` since it wasn't defined */ }
22
+ <span style={ { color: 'blue' } }>Blue</span>
23
+ </CustomSelectItem>
24
+ <CustomSelectItem value="Purple">
25
+ <span style={ { color: 'purple' } }>Purple</span>
26
+ </CustomSelectItem>
27
+ <CustomSelectItem value="Pink">
28
+ <span style={ { color: 'deeppink' } }>Pink</span>
29
+ </CustomSelectItem>
30
+ </CustomSelect>
31
+ );
32
+ ```
33
+
34
+ #### Controlled Mode
35
+
36
+ CustomSelect can also be used in a controlled mode, where the parent component specifies the `value` and the `onChange` props to control selection.
37
+
38
+ ```jsx
39
+ const ControlledCustomSelect = () => {
40
+ const [ value, setValue ] = useState< string | string[] >();
41
+
42
+ const renderControlledValue = ( renderValue: string | string[] ) => (
43
+ <>
44
+ { /* Custom JSX to display `renderValue` item */ }
45
+ </>
46
+ );
47
+
48
+ return (
49
+ <CustomSelect
50
+ { ...props }
51
+ onChange={ ( nextValue ) => {
52
+ setValue( nextValue );
53
+ props.onChange?.( nextValue );
54
+ } }
55
+ value={ value }
56
+ >
57
+ { [ 'blue', 'purple', 'pink' ].map( ( option ) => (
58
+ <CustomSelectItem key={ option } value={ option }>
59
+ { renderControlledValue( option ) }
60
+ </CustomSelectItem>
61
+ ) ) }
62
+ </CustomSelect>
63
+ );
64
+ };
65
+ ```
66
+
67
+ #### Multiple Selection
68
+
69
+ Multiple selection can be enabled by using an array for the `value` and
70
+ `defaultValue` props. The argument of the `onChange` function will also change accordingly.
71
+
72
+ ```jsx
73
+ const MultiSelectCustomSelect = () => (
74
+ <CustomSelect defaultValue={ [ 'blue', 'pink' ] } label="Colors">
75
+ { [ 'blue', 'purple', 'pink' ].map( ( item ) => (
76
+ <CustomSelectItem key={ item } value={ item }>
77
+ { item }
78
+ </CustomSelectItem>
79
+ ) ) }
80
+ </CustomSelect>
81
+ );
82
+ ```
83
+
84
+ ### Components and Sub-components
85
+
86
+ CustomSelect is comprised of two individual components:
87
+
88
+ - `CustomSelect`: a wrapper component and context provider. It is responsible for managing the state of the `CustomSelectItem` children.
89
+ - `CustomSelectItem`: renders a single select item. The first `CustomSelectItem` child will be used as the `defaultValue` when `defaultValue` is undefined.
90
+
9
91
  #### Props
10
92
 
11
93
  The component accepts the following props:
@@ -16,37 +98,45 @@ The child elements. This should be composed of CustomSelect.Item components.
16
98
 
17
99
  - Required: yes
18
100
 
19
- ##### `defaultValue`: `string`
101
+ ##### `defaultValue`: `string | string[]`
20
102
 
21
103
  An optional default value for the control. If left `undefined`, the first non-disabled item will be used.
22
104
 
23
105
  - Required: no
24
106
 
107
+ ##### `hideLabelFromVision`: `boolean`
108
+
109
+ Used to visually hide the label. It will always be visible to screen readers.
110
+
111
+ - Required: no
112
+ - Default: `false`
113
+
25
114
  ##### `label`: `string`
26
115
 
27
116
  Label for the control.
28
117
 
29
118
  - Required: yes
30
119
 
31
- ##### `onChange`: `( newValue: string ) => void`
120
+ ##### `onChange`: `( newValue: string | string[] ) => void`
32
121
 
33
122
  A function that receives the new value of the input.
34
123
 
35
124
  - Required: no
36
125
 
37
- ##### `renderSelectedValue`: `( selectValue: string ) => React.ReactNode`
126
+ ##### `renderSelectedValue`: `( selectValue: string | string[] ) => React.ReactNode`
38
127
 
39
128
  Can be used to render select UI with custom styled values.
40
129
 
41
130
  - Required: no
42
131
 
43
- ##### `size`: `'default' | 'large'`
132
+ ##### `size`: `'default' | 'compact'`
44
133
 
45
134
  The size of the control.
46
135
 
47
136
  - Required: no
137
+ - Default: `'default'`
48
138
 
49
- ##### `value`: `string`
139
+ ##### `value`: `string | string[]`
50
140
 
51
141
  Can be used to externally control the value of the control.
52
142