@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
@@ -0,0 +1,29 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useContext } from '@wordpress/element';
5
+ import { Icon, check } from '@wordpress/icons';
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import type { CustomSelectItemProps } from './types';
10
+ import type { WordPressComponentProps } from '../context';
11
+ import * as Styled from './styles';
12
+ import { CustomSelectContext } from './custom-select';
13
+
14
+ export function CustomSelectItem( {
15
+ children,
16
+ ...props
17
+ }: WordPressComponentProps< CustomSelectItemProps, 'div', false > ) {
18
+ const customSelectContext = useContext( CustomSelectContext );
19
+ return (
20
+ <Styled.SelectItem store={ customSelectContext?.store } { ...props }>
21
+ { children ?? props.value }
22
+ <Styled.SelectedItemCheck>
23
+ <Icon icon={ check } />
24
+ </Styled.SelectedItemCheck>
25
+ </Styled.SelectItem>
26
+ );
27
+ }
28
+
29
+ export default CustomSelectItem;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { createContext, useMemo } from '@wordpress/element';
5
+ import { __, sprintf } from '@wordpress/i18n';
6
+ import { Icon, chevronDown } from '@wordpress/icons';
7
+
8
+ /**
9
+ * Internal dependencies
10
+ */
11
+ import { VisuallyHidden } from '..';
12
+ import * as Styled from './styles';
13
+ import type {
14
+ CustomSelectContext as CustomSelectContextType,
15
+ CustomSelectStore,
16
+ CustomSelectButtonProps,
17
+ _CustomSelectProps,
18
+ } from './types';
19
+ import {
20
+ contextConnectWithoutRef,
21
+ useContextSystem,
22
+ type WordPressComponentProps,
23
+ } from '../context';
24
+
25
+ export const CustomSelectContext =
26
+ createContext< CustomSelectContextType >( undefined );
27
+
28
+ function defaultRenderSelectedValue(
29
+ value: CustomSelectButtonProps[ 'value' ]
30
+ ) {
31
+ const isValueEmpty = Array.isArray( value )
32
+ ? value.length === 0
33
+ : value === undefined || value === null;
34
+
35
+ if ( isValueEmpty ) {
36
+ return __( 'Select an item' );
37
+ }
38
+
39
+ if ( Array.isArray( value ) ) {
40
+ return value.length === 1
41
+ ? value[ 0 ]
42
+ : // translators: %s: number of items selected (it will always be 2 or more items)
43
+ sprintf( __( '%s items selected' ), value.length );
44
+ }
45
+
46
+ return value;
47
+ }
48
+
49
+ const UnconnectedCustomSelectButton = (
50
+ props: Omit<
51
+ WordPressComponentProps<
52
+ CustomSelectButtonProps & CustomSelectStore,
53
+ 'button',
54
+ false
55
+ >,
56
+ 'onChange'
57
+ >
58
+ ) => {
59
+ const {
60
+ renderSelectedValue,
61
+ size = 'default',
62
+ store,
63
+ ...restProps
64
+ } = useContextSystem( props, 'CustomSelectControlButton' );
65
+
66
+ const { value: currentValue } = store.useState();
67
+
68
+ const computedRenderSelectedValue = useMemo(
69
+ () => renderSelectedValue ?? defaultRenderSelectedValue,
70
+ [ renderSelectedValue ]
71
+ );
72
+
73
+ return (
74
+ <Styled.Select
75
+ { ...restProps }
76
+ size={ size }
77
+ hasCustomRenderProp={ !! renderSelectedValue }
78
+ store={ store }
79
+ // to match legacy behavior where using arrow keys
80
+ // move selection rather than open the popover
81
+ showOnKeyDown={ false }
82
+ >
83
+ <div>{ computedRenderSelectedValue( currentValue ) }</div>
84
+ <Icon icon={ chevronDown } size={ 18 } />
85
+ </Styled.Select>
86
+ );
87
+ };
88
+
89
+ const CustomSelectButton = contextConnectWithoutRef(
90
+ UnconnectedCustomSelectButton,
91
+ 'CustomSelectControlButton'
92
+ );
93
+
94
+ function _CustomSelect( props: _CustomSelectProps & CustomSelectStore ) {
95
+ const {
96
+ children,
97
+ hideLabelFromVision = false,
98
+ label,
99
+ store,
100
+ ...restProps
101
+ } = props;
102
+
103
+ return (
104
+ <>
105
+ { hideLabelFromVision ? ( // TODO: Replace with BaseControl
106
+ <VisuallyHidden as="label">{ label }</VisuallyHidden>
107
+ ) : (
108
+ <Styled.SelectLabel store={ store }>
109
+ { label }
110
+ </Styled.SelectLabel>
111
+ ) }
112
+ <CustomSelectButton { ...restProps } store={ store } />
113
+ <Styled.SelectPopover gutter={ 12 } store={ store } sameWidth>
114
+ <CustomSelectContext.Provider value={ { store } }>
115
+ { children }
116
+ </CustomSelectContext.Provider>
117
+ </Styled.SelectPopover>
118
+ </>
119
+ );
120
+ }
121
+
122
+ export default _CustomSelect;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import * as Ariakit from '@ariakit/react';
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import _CustomSelect from '../custom-select';
10
+ import type { CustomSelectProps } from '../types';
11
+
12
+ function CustomSelect( props: CustomSelectProps ) {
13
+ const { defaultValue, onChange, value, ...restProps } = props;
14
+ // Forward props + store from v2 implementation
15
+ const store = Ariakit.useSelectStore( {
16
+ setValue: ( nextValue ) => onChange?.( nextValue ),
17
+ defaultValue,
18
+ value,
19
+ } );
20
+
21
+ return <_CustomSelect { ...restProps } store={ store } />;
22
+ }
23
+
24
+ export default CustomSelect;
@@ -1,105 +1,5 @@
1
- /**
2
- * External dependencies
3
- */
4
- // eslint-disable-next-line no-restricted-imports
5
- import * as Ariakit from '@ariakit/react';
6
- /**
7
- * WordPress dependencies
8
- */
9
- import { createContext, useContext } from '@wordpress/element';
10
- import { __, sprintf } from '@wordpress/i18n';
11
-
12
1
  /**
13
2
  * Internal dependencies
14
3
  */
15
- import * as Styled from './styles';
16
- import type {
17
- CustomSelectProps,
18
- CustomSelectItemProps,
19
- CustomSelectContext as CustomSelectContextType,
20
- } from './types';
21
- import type { WordPressComponentProps } from '../context';
22
-
23
- export const CustomSelectContext =
24
- createContext< CustomSelectContextType >( undefined );
25
-
26
- function defaultRenderSelectedValue( value: CustomSelectProps[ 'value' ] ) {
27
- const isValueEmpty = Array.isArray( value )
28
- ? value.length === 0
29
- : value === undefined || value === null;
30
-
31
- if ( isValueEmpty ) {
32
- return __( 'Select an item' );
33
- }
34
-
35
- if ( Array.isArray( value ) ) {
36
- return value.length === 1
37
- ? value[ 0 ]
38
- : // translators: %s: number of items selected (it will always be 2 or more items)
39
- sprintf( __( '%s items selected' ), value.length );
40
- }
41
-
42
- return value;
43
- }
44
-
45
- export function CustomSelect( {
46
- children,
47
- defaultValue,
48
- label,
49
- onChange,
50
- size = 'default',
51
- value,
52
- renderSelectedValue,
53
- ...props
54
- }: WordPressComponentProps< CustomSelectProps, 'button', false > ) {
55
- const store = Ariakit.useSelectStore( {
56
- setValue: ( nextValue ) => onChange?.( nextValue ),
57
- defaultValue,
58
- value,
59
- // fix for Safari bug: https://github.com/WordPress/gutenberg/issues/55023#issuecomment-1834035917
60
- virtualFocus: false,
61
- } );
62
-
63
- const { value: currentValue } = store.useState();
64
-
65
- const computedRenderSelectedValue =
66
- renderSelectedValue ?? defaultRenderSelectedValue;
67
-
68
- return (
69
- <>
70
- <Styled.CustomSelectLabel store={ store }>
71
- { label }
72
- </Styled.CustomSelectLabel>
73
- <Styled.CustomSelectButton
74
- { ...props }
75
- size={ size }
76
- hasCustomRenderProp={ !! renderSelectedValue }
77
- store={ store }
78
- >
79
- { computedRenderSelectedValue( currentValue ) }
80
- <Ariakit.SelectArrow />
81
- </Styled.CustomSelectButton>
82
- <Styled.CustomSelectPopover gutter={ 12 } store={ store } sameWidth>
83
- <CustomSelectContext.Provider value={ { store } }>
84
- { children }
85
- </CustomSelectContext.Provider>
86
- </Styled.CustomSelectPopover>
87
- </>
88
- );
89
- }
90
-
91
- export function CustomSelectItem( {
92
- children,
93
- ...props
94
- }: WordPressComponentProps< CustomSelectItemProps, 'div', false > ) {
95
- const customSelectContext = useContext( CustomSelectContext );
96
- return (
97
- <Styled.CustomSelectItem
98
- store={ customSelectContext?.store }
99
- { ...props }
100
- >
101
- { children ?? props.value }
102
- <Ariakit.SelectItemCheck />
103
- </Styled.CustomSelectItem>
104
- );
105
- }
4
+ export { default as CustomSelect } from './legacy-adapter';
5
+ export { default as CustomSelectItem } from './custom-select-item';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import _LegacyCustomSelect from './legacy-component';
5
+ import _NewCustomSelect from './default-component';
6
+ import type { CustomSelectProps, LegacyCustomSelectProps } from './types';
7
+ import type { WordPressComponentProps } from '../context';
8
+
9
+ function isLegacy( props: any ): props is LegacyCustomSelectProps {
10
+ return typeof props.options !== 'undefined';
11
+ }
12
+
13
+ function CustomSelect(
14
+ props:
15
+ | LegacyCustomSelectProps
16
+ | WordPressComponentProps< CustomSelectProps, 'button', false >
17
+ ) {
18
+ if ( isLegacy( props ) ) {
19
+ return <_LegacyCustomSelect { ...props } />;
20
+ }
21
+
22
+ return <_NewCustomSelect { ...props } />;
23
+ }
24
+
25
+ export default CustomSelect;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import * as Ariakit from '@ariakit/react';
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useMemo } from '@wordpress/element';
10
+ /**
11
+ * Internal dependencies
12
+ */
13
+ import _CustomSelect from '../custom-select';
14
+ import type { LegacyCustomSelectProps } from '../types';
15
+ import { CustomSelectItem } from '..';
16
+ import * as Styled from '../styles';
17
+ import { ContextSystemProvider } from '../../context';
18
+
19
+ function CustomSelect( props: LegacyCustomSelectProps ) {
20
+ const {
21
+ __experimentalShowSelectedHint,
22
+ __next40pxDefaultSize = false,
23
+ options,
24
+ onChange,
25
+ size = 'default',
26
+ value,
27
+ ...restProps
28
+ } = props;
29
+
30
+ // Forward props + store from v2 implementation
31
+ const store = Ariakit.useSelectStore( {
32
+ async setValue( nextValue ) {
33
+ if ( ! onChange ) return;
34
+
35
+ // Executes the logic in a microtask after the popup is closed.
36
+ // This is simply to ensure the isOpen state matches that in Downshift.
37
+ await Promise.resolve();
38
+ const state = store.getState();
39
+
40
+ const changeObject = {
41
+ highlightedIndex: state.renderedItems.findIndex(
42
+ ( item ) => item.value === nextValue
43
+ ),
44
+ inputValue: '',
45
+ isOpen: state.open,
46
+ selectedItem: {
47
+ name: nextValue as string,
48
+ key: nextValue as string,
49
+ },
50
+ type: '',
51
+ };
52
+ onChange( changeObject );
53
+ },
54
+ } );
55
+
56
+ const children = options.map(
57
+ ( { name, key, __experimentalHint, ...rest } ) => {
58
+ const withHint = (
59
+ <Styled.WithHintWrapper>
60
+ <span>{ name }</span>
61
+ <Styled.ExperimentalHintItem className="components-custom-select-control__item-hint">
62
+ { __experimentalHint }
63
+ </Styled.ExperimentalHintItem>
64
+ </Styled.WithHintWrapper>
65
+ );
66
+
67
+ return (
68
+ <CustomSelectItem
69
+ key={ key }
70
+ value={ name }
71
+ children={
72
+ __experimentalShowSelectedHint ? withHint : name
73
+ }
74
+ { ...rest }
75
+ />
76
+ );
77
+ }
78
+ );
79
+
80
+ const renderSelectedValueHint = () => {
81
+ const { value: currentValue } = store.getState();
82
+
83
+ const currentHint = options?.find(
84
+ ( { name } ) => currentValue === name
85
+ );
86
+
87
+ return (
88
+ <>
89
+ { currentValue }
90
+ <Styled.SelectedExperimentalHintItem className="components-custom-select-control__hint">
91
+ { currentHint?.__experimentalHint }
92
+ </Styled.SelectedExperimentalHintItem>
93
+ </>
94
+ );
95
+ };
96
+
97
+ // translate legacy button sizing
98
+ const contextSystemValue = useMemo( () => {
99
+ let selectedSize;
100
+
101
+ if (
102
+ ( __next40pxDefaultSize && size === 'default' ) ||
103
+ size === '__unstable-large'
104
+ ) {
105
+ selectedSize = 'default';
106
+ } else if ( ! __next40pxDefaultSize && size === 'default' ) {
107
+ selectedSize = 'compact';
108
+ } else {
109
+ selectedSize = size;
110
+ }
111
+
112
+ return {
113
+ CustomSelectControlButton: { _overrides: { size: selectedSize } },
114
+ };
115
+ }, [ __next40pxDefaultSize, size ] );
116
+
117
+ const translatedProps = {
118
+ 'aria-describedby': props.describedBy,
119
+ children,
120
+ renderSelectedValue: __experimentalShowSelectedHint
121
+ ? renderSelectedValueHint
122
+ : undefined,
123
+ ...restProps,
124
+ };
125
+
126
+ return (
127
+ <ContextSystemProvider value={ contextSystemValue }>
128
+ <_CustomSelect { ...translatedProps } store={ store } />
129
+ </ContextSystemProvider>
130
+ );
131
+ }
132
+
133
+ export default CustomSelect;
@@ -11,10 +11,11 @@ import { useState } from '@wordpress/element';
11
11
  /**
12
12
  * Internal dependencies
13
13
  */
14
- import { CustomSelect, CustomSelectItem } from '..';
14
+ import CustomSelect from '../default-component';
15
+ import { CustomSelectItem } from '..';
15
16
 
16
17
  const meta: Meta< typeof CustomSelect > = {
17
- title: 'Components (Experimental)/CustomSelectControl v2',
18
+ title: 'Components (Experimental)/CustomSelectControl v2/Default',
18
19
  component: CustomSelect,
19
20
  subcomponents: {
20
21
  // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
@@ -22,15 +23,14 @@ const meta: Meta< typeof CustomSelect > = {
22
23
  },
23
24
  argTypes: {
24
25
  children: { control: { type: null } },
25
- renderSelectedValue: { control: { type: null } },
26
26
  value: { control: { type: null } },
27
27
  },
28
+ tags: [ 'status-wip' ],
28
29
  parameters: {
29
30
  badges: [ 'wip' ],
30
31
  actions: { argTypesRegex: '^on.*' },
31
32
  controls: { expanded: true },
32
33
  docs: {
33
- canvas: { sourceState: 'shown' },
34
34
  source: { excludeDecorators: true },
35
35
  },
36
36
  },
@@ -49,15 +49,11 @@ const meta: Meta< typeof CustomSelect > = {
49
49
  export default meta;
50
50
 
51
51
  const Template: StoryFn< typeof CustomSelect > = ( props ) => {
52
- return <CustomSelect { ...props } />;
53
- };
54
-
55
- const ControlledTemplate: StoryFn< typeof CustomSelect > = ( props ) => {
56
52
  const [ value, setValue ] = useState< string | string[] >();
57
53
  return (
58
54
  <CustomSelect
59
55
  { ...props }
60
- onChange={ ( nextValue ) => {
56
+ onChange={ ( nextValue: string | string[] ) => {
61
57
  setValue( nextValue );
62
58
  props.onChange?.( nextValue );
63
59
  } }
@@ -68,14 +64,18 @@ const ControlledTemplate: StoryFn< typeof CustomSelect > = ( props ) => {
68
64
 
69
65
  export const Default = Template.bind( {} );
70
66
  Default.args = {
71
- label: 'Label',
67
+ label: 'Label text',
68
+ defaultValue: 'Select a color...',
72
69
  children: (
73
70
  <>
74
- <CustomSelectItem value="Small">
75
- <span style={ { fontSize: '75%' } }>Small</span>
71
+ <CustomSelectItem value="Blue">
72
+ <span style={ { color: 'blue' } }>Blue</span>
76
73
  </CustomSelectItem>
77
- <CustomSelectItem value="Something bigger">
78
- <span style={ { fontSize: '200%' } }>Something bigger</span>
74
+ <CustomSelectItem value="Purple">
75
+ <span style={ { color: 'purple' } }>Purple</span>
76
+ </CustomSelectItem>
77
+ <CustomSelectItem value="Pink">
78
+ <span style={ { color: 'deeppink' } }>Pink</span>
79
79
  </CustomSelectItem>
80
80
  </>
81
81
  ),
@@ -83,21 +83,13 @@ Default.args = {
83
83
 
84
84
  /**
85
85
  * Multiple selection can be enabled by using an array for the `value` and
86
- * `defaultValue` props. The argument of the `onChange` function will also
86
+ * `defaultValue` props. The argument type of the `onChange` function will also
87
87
  * change accordingly.
88
88
  */
89
- export const MultiSelect = Template.bind( {} );
90
- MultiSelect.args = {
91
- defaultValue: [ 'lavender', 'tangerine' ],
89
+ export const MultipleSelection = Template.bind( {} );
90
+ MultipleSelection.args = {
92
91
  label: 'Select Colors',
93
- renderSelectedValue: ( currentValue: string | string[] ) => {
94
- if ( ! Array.isArray( currentValue ) ) {
95
- return currentValue;
96
- }
97
- if ( currentValue.length === 0 ) return 'No colors selected';
98
- if ( currentValue.length === 1 ) return currentValue[ 0 ];
99
- return `${ currentValue.length } colors selected`;
100
- },
92
+ defaultValue: [ 'lavender', 'tangerine' ],
101
93
  children: (
102
94
  <>
103
95
  { [
@@ -116,32 +108,34 @@ MultiSelect.args = {
116
108
  ),
117
109
  };
118
110
 
119
- const renderControlledValue = ( gravatar: string | string[] ) => {
111
+ const renderItem = ( gravatar: string | string[] ) => {
120
112
  const avatar = `https://gravatar.com/avatar?d=${ gravatar }`;
121
113
  return (
122
114
  <div style={ { display: 'flex', alignItems: 'center' } }>
123
115
  <img
124
116
  style={ { maxHeight: '75px', marginRight: '10px' } }
125
- key={ avatar }
126
117
  src={ avatar }
127
118
  alt=""
128
- aria-hidden="true"
129
119
  />
130
120
  <span>{ gravatar }</span>
131
121
  </div>
132
122
  );
133
123
  };
134
124
 
135
- export const Controlled = ControlledTemplate.bind( {} );
136
- Controlled.args = {
125
+ /**
126
+ * The `renderSelectedValue` prop can be used to customize how the selected value
127
+ * is rendered in the dropdown trigger.
128
+ */
129
+ export const CustomSelectedValue = Template.bind( {} );
130
+ CustomSelectedValue.args = {
137
131
  label: 'Default Gravatars',
138
- renderSelectedValue: renderControlledValue,
132
+ renderSelectedValue: renderItem,
139
133
  children: (
140
134
  <>
141
135
  { [ 'mystery-person', 'identicon', 'wavatar', 'retro' ].map(
142
136
  ( option ) => (
143
137
  <CustomSelectItem key={ option } value={ option }>
144
- { renderControlledValue( option ) }
138
+ { renderItem( option ) }
145
139
  </CustomSelectItem>
146
140
  )
147
141
  ) }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import type { Meta, StoryFn } from '@storybook/react';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useState } from '@wordpress/element';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import _LegacyCustomSelect from '../legacy-component';
15
+ import { CustomSelect } from '..';
16
+
17
+ const meta: Meta< typeof _LegacyCustomSelect > = {
18
+ title: 'Components (Experimental)/CustomSelectControl v2/Legacy',
19
+ component: _LegacyCustomSelect,
20
+ argTypes: {
21
+ onChange: { control: { type: null } },
22
+ value: { control: { type: null } },
23
+ },
24
+ parameters: {
25
+ badges: [ 'wip' ],
26
+ actions: { argTypesRegex: '^on.*' },
27
+ controls: { expanded: true },
28
+ docs: {
29
+ source: { excludeDecorators: true },
30
+ },
31
+ },
32
+ decorators: [
33
+ ( Story ) => (
34
+ <div
35
+ style={ {
36
+ minHeight: '150px',
37
+ } }
38
+ >
39
+ <Story />
40
+ </div>
41
+ ),
42
+ ],
43
+ };
44
+ export default meta;
45
+
46
+ const Template: StoryFn< typeof _LegacyCustomSelect > = ( props ) => {
47
+ const [ fontSize, setFontSize ] = useState( props.options[ 0 ] );
48
+
49
+ const onChange: React.ComponentProps<
50
+ typeof _LegacyCustomSelect
51
+ >[ 'onChange' ] = ( changeObject ) => {
52
+ setFontSize( changeObject.selectedItem );
53
+ props.onChange?.( changeObject );
54
+ };
55
+
56
+ return (
57
+ <CustomSelect { ...props } onChange={ onChange } value={ fontSize } />
58
+ );
59
+ };
60
+
61
+ export const Default = Template.bind( {} );
62
+ Default.args = {
63
+ label: 'Label text',
64
+ options: [
65
+ {
66
+ key: 'small',
67
+ name: 'Small',
68
+ style: { fontSize: '50%' },
69
+ __experimentalHint: '50%',
70
+ },
71
+ {
72
+ key: 'normal',
73
+ name: 'Normal',
74
+ style: { fontSize: '100%' },
75
+ className: 'can-apply-custom-class-to-option',
76
+ },
77
+ {
78
+ key: 'large',
79
+ name: 'Large',
80
+ style: { fontSize: '200%' },
81
+ },
82
+ {
83
+ key: 'huge',
84
+ name: 'Huge',
85
+ style: { fontSize: '300%' },
86
+ },
87
+ ],
88
+ };