@wordpress/components 29.13.1-next.719a03cbe.0 → 30.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 (233) hide show
  1. package/CHANGELOG.md +14 -3
  2. package/build/autocomplete/index.js +4 -0
  3. package/build/autocomplete/index.js.map +1 -1
  4. package/build/calendar/date-calendar/index.js +13 -4
  5. package/build/calendar/date-calendar/index.js.map +1 -1
  6. package/build/calendar/date-range-calendar/index.js +8 -4
  7. package/build/calendar/date-range-calendar/index.js.map +1 -1
  8. package/build/calendar/types.js.map +1 -1
  9. package/build/icon/index.js +2 -0
  10. package/build/icon/index.js.map +1 -1
  11. package/build/index.js +0 -19
  12. package/build/index.js.map +1 -1
  13. package/build/private-apis.js +5 -1
  14. package/build/private-apis.js.map +1 -1
  15. package/build/select-control/index.js +1 -1
  16. package/build/select-control/index.js.map +1 -1
  17. package/build/toggle-group-control/toggle-group-control/as-button-group.js.map +1 -1
  18. package/build/utils/hooks/use-controlled-value.js +8 -4
  19. package/build/utils/hooks/use-controlled-value.js.map +1 -1
  20. package/build/validated-form-controls/components/checkbox-control.js +52 -0
  21. package/build/validated-form-controls/components/checkbox-control.js.map +1 -0
  22. package/build/validated-form-controls/components/combobox-control.js +64 -0
  23. package/build/validated-form-controls/components/combobox-control.js.map +1 -0
  24. package/build/validated-form-controls/components/custom-select-control.js +71 -0
  25. package/build/validated-form-controls/components/custom-select-control.js.map +1 -0
  26. package/build/validated-form-controls/components/index.js +138 -0
  27. package/build/validated-form-controls/components/index.js.map +1 -0
  28. package/build/validated-form-controls/components/input-control.js +50 -0
  29. package/build/validated-form-controls/components/input-control.js.map +1 -0
  30. package/build/validated-form-controls/components/number-control.js +53 -0
  31. package/build/validated-form-controls/components/number-control.js.map +1 -0
  32. package/build/validated-form-controls/components/radio-control.js +51 -0
  33. package/build/validated-form-controls/components/radio-control.js.map +1 -0
  34. package/build/validated-form-controls/components/range-control.js +51 -0
  35. package/build/validated-form-controls/components/range-control.js.map +1 -0
  36. package/build/validated-form-controls/components/select-control.js +53 -0
  37. package/build/validated-form-controls/components/select-control.js.map +1 -0
  38. package/build/validated-form-controls/components/text-control.js +51 -0
  39. package/build/validated-form-controls/components/text-control.js.map +1 -0
  40. package/build/validated-form-controls/components/textarea-control.js +50 -0
  41. package/build/validated-form-controls/components/textarea-control.js.map +1 -0
  42. package/build/validated-form-controls/components/toggle-control.js +60 -0
  43. package/build/validated-form-controls/components/toggle-control.js.map +1 -0
  44. package/build/validated-form-controls/components/toggle-group-control.js +69 -0
  45. package/build/validated-form-controls/components/toggle-group-control.js.map +1 -0
  46. package/build/validated-form-controls/components/types.js +6 -0
  47. package/build/validated-form-controls/components/types.js.map +1 -0
  48. package/build/validated-form-controls/control-with-error.js +137 -0
  49. package/build/validated-form-controls/control-with-error.js.map +1 -0
  50. package/build/validated-form-controls/index.js +28 -0
  51. package/build/validated-form-controls/index.js.map +1 -0
  52. package/build-module/autocomplete/index.js +4 -0
  53. package/build-module/autocomplete/index.js.map +1 -1
  54. package/build-module/calendar/date-calendar/index.js +11 -3
  55. package/build-module/calendar/date-calendar/index.js.map +1 -1
  56. package/build-module/calendar/date-range-calendar/index.js +8 -4
  57. package/build-module/calendar/date-range-calendar/index.js.map +1 -1
  58. package/build-module/calendar/types.js.map +1 -1
  59. package/build-module/icon/index.js +2 -0
  60. package/build-module/icon/index.js.map +1 -1
  61. package/build-module/index.js +0 -1
  62. package/build-module/index.js.map +1 -1
  63. package/build-module/private-apis.js +5 -1
  64. package/build-module/private-apis.js.map +1 -1
  65. package/build-module/select-control/index.js +1 -1
  66. package/build-module/select-control/index.js.map +1 -1
  67. package/build-module/toggle-group-control/toggle-group-control/as-button-group.js.map +1 -1
  68. package/build-module/utils/hooks/use-controlled-value.js +9 -5
  69. package/build-module/utils/hooks/use-controlled-value.js.map +1 -1
  70. package/build-module/validated-form-controls/components/checkbox-control.js +44 -0
  71. package/build-module/validated-form-controls/components/checkbox-control.js.map +1 -0
  72. package/build-module/validated-form-controls/components/combobox-control.js +56 -0
  73. package/build-module/validated-form-controls/components/combobox-control.js.map +1 -0
  74. package/build-module/validated-form-controls/components/custom-select-control.js +63 -0
  75. package/build-module/validated-form-controls/components/custom-select-control.js.map +1 -0
  76. package/build-module/validated-form-controls/components/index.js +13 -0
  77. package/build-module/validated-form-controls/components/index.js.map +1 -0
  78. package/build-module/validated-form-controls/components/input-control.js +42 -0
  79. package/build-module/validated-form-controls/components/input-control.js.map +1 -0
  80. package/build-module/validated-form-controls/components/number-control.js +45 -0
  81. package/build-module/validated-form-controls/components/number-control.js.map +1 -0
  82. package/build-module/validated-form-controls/components/radio-control.js +43 -0
  83. package/build-module/validated-form-controls/components/radio-control.js.map +1 -0
  84. package/build-module/validated-form-controls/components/range-control.js +43 -0
  85. package/build-module/validated-form-controls/components/range-control.js.map +1 -0
  86. package/build-module/validated-form-controls/components/select-control.js +45 -0
  87. package/build-module/validated-form-controls/components/select-control.js.map +1 -0
  88. package/build-module/validated-form-controls/components/text-control.js +43 -0
  89. package/build-module/validated-form-controls/components/text-control.js.map +1 -0
  90. package/build-module/validated-form-controls/components/textarea-control.js +42 -0
  91. package/build-module/validated-form-controls/components/textarea-control.js.map +1 -0
  92. package/build-module/validated-form-controls/components/toggle-control.js +52 -0
  93. package/build-module/validated-form-controls/components/toggle-control.js.map +1 -0
  94. package/build-module/validated-form-controls/components/toggle-group-control.js +62 -0
  95. package/build-module/validated-form-controls/components/toggle-group-control.js.map +1 -0
  96. package/build-module/validated-form-controls/components/types.js +2 -0
  97. package/build-module/validated-form-controls/components/types.js.map +1 -0
  98. package/build-module/validated-form-controls/control-with-error.js +129 -0
  99. package/build-module/validated-form-controls/control-with-error.js.map +1 -0
  100. package/build-module/validated-form-controls/index.js +3 -0
  101. package/build-module/validated-form-controls/index.js.map +1 -0
  102. package/build-style/style-rtl.css +60 -17
  103. package/build-style/style.css +60 -17
  104. package/build-types/autocomplete/index.d.ts.map +1 -1
  105. package/build-types/calendar/date-calendar/index.d.ts.map +1 -1
  106. package/build-types/calendar/date-range-calendar/index.d.ts.map +1 -1
  107. package/build-types/calendar/types.d.ts +2 -2
  108. package/build-types/calendar/types.d.ts.map +1 -1
  109. package/build-types/color-picker/styles.d.ts.map +1 -1
  110. package/build-types/icon/index.d.ts.map +1 -1
  111. package/build-types/index.d.ts +0 -1
  112. package/build-types/index.d.ts.map +1 -1
  113. package/build-types/private-apis.d.ts.map +1 -1
  114. package/build-types/select-control/stories/index.story.d.ts.map +1 -1
  115. package/build-types/toggle-group-control/toggle-group-control/as-button-group.d.ts.map +1 -1
  116. package/build-types/utils/hooks/use-controlled-value.d.ts +2 -2
  117. package/build-types/utils/hooks/use-controlled-value.d.ts.map +1 -1
  118. package/build-types/validated-form-controls/components/checkbox-control.d.ts +9 -0
  119. package/build-types/validated-form-controls/components/checkbox-control.d.ts.map +1 -0
  120. package/build-types/validated-form-controls/components/combobox-control.d.ts +21 -0
  121. package/build-types/validated-form-controls/components/combobox-control.d.ts.map +1 -0
  122. package/build-types/validated-form-controls/components/custom-select-control.d.ts +4 -0
  123. package/build-types/validated-form-controls/components/custom-select-control.d.ts.map +1 -0
  124. package/build-types/validated-form-controls/components/index.d.ts +13 -0
  125. package/build-types/validated-form-controls/components/index.d.ts.map +1 -0
  126. package/build-types/validated-form-controls/components/input-control.d.ts +4 -0
  127. package/build-types/validated-form-controls/components/input-control.d.ts.map +1 -0
  128. package/build-types/validated-form-controls/components/number-control.d.ts +17 -0
  129. package/build-types/validated-form-controls/components/number-control.d.ts.map +1 -0
  130. package/build-types/validated-form-controls/components/radio-control.d.ts +11 -0
  131. package/build-types/validated-form-controls/components/radio-control.d.ts.map +1 -0
  132. package/build-types/validated-form-controls/components/range-control.d.ts +36 -0
  133. package/build-types/validated-form-controls/components/range-control.d.ts.map +1 -0
  134. package/build-types/validated-form-controls/components/select-control.d.ts +9 -0
  135. package/build-types/validated-form-controls/components/select-control.d.ts.map +1 -0
  136. package/build-types/validated-form-controls/components/stories/checkbox-control.story.d.ts +12 -0
  137. package/build-types/validated-form-controls/components/stories/checkbox-control.story.d.ts.map +1 -0
  138. package/build-types/validated-form-controls/components/stories/combobox-control.story.d.ts +12 -0
  139. package/build-types/validated-form-controls/components/stories/combobox-control.story.d.ts.map +1 -0
  140. package/build-types/validated-form-controls/components/stories/custom-select-control.story.d.ts +12 -0
  141. package/build-types/validated-form-controls/components/stories/custom-select-control.story.d.ts.map +1 -0
  142. package/build-types/validated-form-controls/components/stories/input-control.story.d.ts +18 -0
  143. package/build-types/validated-form-controls/components/stories/input-control.story.d.ts.map +1 -0
  144. package/build-types/validated-form-controls/components/stories/number-control.story.d.ts +12 -0
  145. package/build-types/validated-form-controls/components/stories/number-control.story.d.ts.map +1 -0
  146. package/build-types/validated-form-controls/components/stories/overview.story.d.ts +19 -0
  147. package/build-types/validated-form-controls/components/stories/overview.story.d.ts.map +1 -0
  148. package/build-types/validated-form-controls/components/stories/radio-control.story.d.ts +12 -0
  149. package/build-types/validated-form-controls/components/stories/radio-control.story.d.ts.map +1 -0
  150. package/build-types/validated-form-controls/components/stories/range-control.story.d.ts +9 -0
  151. package/build-types/validated-form-controls/components/stories/range-control.story.d.ts.map +1 -0
  152. package/build-types/validated-form-controls/components/stories/select-control.story.d.ts +12 -0
  153. package/build-types/validated-form-controls/components/stories/select-control.story.d.ts.map +1 -0
  154. package/build-types/validated-form-controls/components/stories/story-utils.d.ts +9 -0
  155. package/build-types/validated-form-controls/components/stories/story-utils.d.ts.map +1 -0
  156. package/build-types/validated-form-controls/components/stories/text-control.story.d.ts +9 -0
  157. package/build-types/validated-form-controls/components/stories/text-control.story.d.ts.map +1 -0
  158. package/build-types/validated-form-controls/components/stories/textarea-control.story.d.ts +9 -0
  159. package/build-types/validated-form-controls/components/stories/textarea-control.story.d.ts.map +1 -0
  160. package/build-types/validated-form-controls/components/stories/toggle-control.story.d.ts +9 -0
  161. package/build-types/validated-form-controls/components/stories/toggle-control.story.d.ts.map +1 -0
  162. package/build-types/validated-form-controls/components/stories/toggle-group-control.story.d.ts +9 -0
  163. package/build-types/validated-form-controls/components/stories/toggle-group-control.story.d.ts.map +1 -0
  164. package/build-types/validated-form-controls/components/text-control.d.ts +8 -0
  165. package/build-types/validated-form-controls/components/text-control.d.ts.map +1 -0
  166. package/build-types/validated-form-controls/components/textarea-control.d.ts +7 -0
  167. package/build-types/validated-form-controls/components/textarea-control.d.ts.map +1 -0
  168. package/build-types/validated-form-controls/components/toggle-control.d.ts +7 -0
  169. package/build-types/validated-form-controls/components/toggle-control.d.ts.map +1 -0
  170. package/build-types/validated-form-controls/components/toggle-group-control.d.ts +15 -0
  171. package/build-types/validated-form-controls/components/toggle-group-control.d.ts.map +1 -0
  172. package/build-types/validated-form-controls/components/types.d.ts +27 -0
  173. package/build-types/validated-form-controls/components/types.d.ts.map +1 -0
  174. package/build-types/validated-form-controls/control-with-error.d.ts +36 -0
  175. package/build-types/validated-form-controls/control-with-error.d.ts.map +1 -0
  176. package/build-types/validated-form-controls/index.d.ts +3 -0
  177. package/build-types/validated-form-controls/index.d.ts.map +1 -0
  178. package/package.json +19 -19
  179. package/src/autocomplete/index.tsx +4 -0
  180. package/src/calendar/date-calendar/README.md +57 -46
  181. package/src/calendar/date-calendar/index.tsx +22 -8
  182. package/src/calendar/date-range-calendar/README.md +63 -52
  183. package/src/calendar/date-range-calendar/index.tsx +23 -11
  184. package/src/calendar/types.ts +2 -2
  185. package/src/dimension-control/test/__snapshots__/index.test.js.snap +8 -8
  186. package/src/icon/index.tsx +2 -0
  187. package/src/index.ts +0 -1
  188. package/src/private-apis.ts +5 -0
  189. package/src/select-control/index.tsx +1 -1
  190. package/src/style.scss +2 -2
  191. package/src/toggle-group-control/toggle-group-control/as-button-group.tsx +3 -1
  192. package/src/utils/hooks/use-controlled-value.ts +16 -8
  193. package/src/validated-form-controls/components/checkbox-control.tsx +64 -0
  194. package/src/validated-form-controls/components/combobox-control.tsx +77 -0
  195. package/src/validated-form-controls/components/custom-select-control.tsx +86 -0
  196. package/src/validated-form-controls/components/index.ts +12 -0
  197. package/src/validated-form-controls/components/input-control.tsx +59 -0
  198. package/src/validated-form-controls/components/number-control.tsx +61 -0
  199. package/src/validated-form-controls/components/radio-control.tsx +60 -0
  200. package/src/validated-form-controls/components/range-control.tsx +60 -0
  201. package/src/validated-form-controls/components/select-control.tsx +75 -0
  202. package/src/validated-form-controls/components/stories/checkbox-control.story.tsx +57 -0
  203. package/src/validated-form-controls/components/stories/combobox-control.story.tsx +64 -0
  204. package/src/validated-form-controls/components/stories/custom-select-control.story.tsx +64 -0
  205. package/src/validated-form-controls/components/stories/input-control.story.tsx +132 -0
  206. package/src/validated-form-controls/components/stories/number-control.story.tsx +62 -0
  207. package/src/validated-form-controls/components/stories/overview.mdx +52 -0
  208. package/src/validated-form-controls/components/stories/overview.story.tsx +100 -0
  209. package/src/validated-form-controls/components/stories/radio-control.story.tsx +64 -0
  210. package/src/validated-form-controls/components/stories/range-control.story.tsx +60 -0
  211. package/src/validated-form-controls/components/stories/select-control.story.tsx +60 -0
  212. package/src/validated-form-controls/components/stories/story-utils.tsx +46 -0
  213. package/src/validated-form-controls/components/stories/text-control.story.tsx +55 -0
  214. package/src/validated-form-controls/components/stories/textarea-control.story.tsx +52 -0
  215. package/src/validated-form-controls/components/stories/toggle-control.story.tsx +55 -0
  216. package/src/validated-form-controls/components/stories/toggle-group-control.story.tsx +66 -0
  217. package/src/validated-form-controls/components/text-control.tsx +60 -0
  218. package/src/validated-form-controls/components/textarea-control.tsx +59 -0
  219. package/src/validated-form-controls/components/toggle-control.tsx +69 -0
  220. package/src/validated-form-controls/components/toggle-group-control.tsx +82 -0
  221. package/src/validated-form-controls/components/types.ts +28 -0
  222. package/src/validated-form-controls/control-with-error.tsx +198 -0
  223. package/src/validated-form-controls/index.ts +2 -0
  224. package/src/validated-form-controls/style.scss +75 -0
  225. package/tsconfig.tsbuildinfo +1 -1
  226. package/build/calendar/utils/use-controlled-value.js +0 -58
  227. package/build/calendar/utils/use-controlled-value.js.map +0 -1
  228. package/build-module/calendar/utils/use-controlled-value.js +0 -51
  229. package/build-module/calendar/utils/use-controlled-value.js.map +0 -1
  230. package/build-types/calendar/utils/use-controlled-value.d.ts +0 -27
  231. package/build-types/calendar/utils/use-controlled-value.d.ts.map +0 -1
  232. package/src/calendar/utils/use-controlled-value.ts +0 -61
  233. package/src/dimension-control/style.scss +0 -22
@@ -0,0 +1,77 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useMergeRefs } from '@wordpress/compose';
5
+ import { forwardRef, useEffect, useRef } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { ControlWithError } from '../control-with-error';
11
+ import type { ValidatedControlProps } from './types';
12
+ import ComboboxControl from '../../combobox-control';
13
+ import type { ComboboxControlProps } from '../../combobox-control/types';
14
+
15
+ type Value = ComboboxControlProps[ 'value' ];
16
+
17
+ const UnforwardedValidatedComboboxControl = (
18
+ {
19
+ required,
20
+ customValidator,
21
+ onChange,
22
+ markWhenOptional,
23
+ ...restProps
24
+ }: Omit<
25
+ React.ComponentProps< typeof ComboboxControl >,
26
+ '__next40pxDefaultSize' | '__nextHasNoMarginBottom'
27
+ > &
28
+ ValidatedControlProps< Value >,
29
+ forwardedRef: React.ForwardedRef< HTMLInputElement >
30
+ ) => {
31
+ const validityTargetRef = useRef< HTMLInputElement >( null );
32
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
33
+ const valueRef = useRef< Value >( restProps.value );
34
+
35
+ // TODO: Upstream limitation - The `required` attribute is not passed down to the input,
36
+ // so we need to set it manually.
37
+ useEffect( () => {
38
+ const input =
39
+ validityTargetRef.current?.querySelector< HTMLInputElement >(
40
+ 'input[role="combobox"]'
41
+ );
42
+ if ( input ) {
43
+ input.required = required ?? false;
44
+ }
45
+ }, [ required ] );
46
+
47
+ return (
48
+ // TODO: Bug - Missing value error is not cleared immediately on change, waits for blur.
49
+ <ControlWithError
50
+ required={ required }
51
+ markWhenOptional={ markWhenOptional }
52
+ ref={ mergedRefs }
53
+ customValidator={ () => {
54
+ return customValidator?.( valueRef.current );
55
+ } }
56
+ getValidityTarget={ () =>
57
+ validityTargetRef.current?.querySelector< HTMLInputElement >(
58
+ 'input[role="combobox"]'
59
+ )
60
+ }
61
+ >
62
+ <ComboboxControl
63
+ __nextHasNoMarginBottom
64
+ __next40pxDefaultSize
65
+ { ...restProps }
66
+ onChange={ ( value ) => {
67
+ valueRef.current = value;
68
+ onChange?.( value );
69
+ } }
70
+ />
71
+ </ControlWithError>
72
+ );
73
+ };
74
+
75
+ export const ValidatedComboboxControl = forwardRef(
76
+ UnforwardedValidatedComboboxControl
77
+ );
@@ -0,0 +1,86 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { forwardRef, useRef } from '@wordpress/element';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { ControlWithError } from '../control-with-error';
10
+ import type { ValidatedControlProps } from './types';
11
+ import CustomSelectControl from '../../custom-select-control';
12
+ import type {
13
+ CustomSelectOption,
14
+ CustomSelectProps,
15
+ } from '../../custom-select-control/types';
16
+
17
+ type CustomSelectControlProps = CustomSelectProps< CustomSelectOption >;
18
+
19
+ type Value = CustomSelectControlProps[ 'value' ];
20
+
21
+ const UnforwardedValidatedCustomSelectControl = (
22
+ {
23
+ required,
24
+ customValidator,
25
+ onChange,
26
+ markWhenOptional,
27
+ ...restProps
28
+ }: Omit<
29
+ React.ComponentProps< typeof CustomSelectControl >,
30
+ '__next40pxDefaultSize'
31
+ > &
32
+ ValidatedControlProps< Value >,
33
+ forwardedRef: React.ForwardedRef< HTMLDivElement >
34
+ ) => {
35
+ const validityTargetRef = useRef< HTMLSelectElement >( null );
36
+ const valueRef = useRef< Value >( restProps.value );
37
+
38
+ return (
39
+ <div
40
+ className="components-validated-control__wrapper-with-error-delegate"
41
+ ref={ forwardedRef }
42
+ >
43
+ <ControlWithError
44
+ required={ required }
45
+ markWhenOptional={ markWhenOptional }
46
+ customValidator={ () => {
47
+ return customValidator?.( valueRef.current );
48
+ } }
49
+ getValidityTarget={ () => validityTargetRef.current }
50
+ >
51
+ <CustomSelectControl
52
+ // TODO: Upstream limitation - Required isn't passed down correctly,
53
+ // so it needs to be set on a delegate element.
54
+ __next40pxDefaultSize
55
+ onChange={ ( value ) => {
56
+ valueRef.current = value.selectedItem;
57
+ onChange?.( value );
58
+ } }
59
+ { ...restProps }
60
+ />
61
+ </ControlWithError>
62
+ <select
63
+ className="components-validated-control__error-delegate"
64
+ ref={ validityTargetRef }
65
+ required={ required }
66
+ tabIndex={ -1 }
67
+ value={ restProps.value?.key ? 'hasvalue' : '' }
68
+ onChange={ () => {} }
69
+ onFocus={ ( e ) => {
70
+ e.target.previousElementSibling
71
+ ?.querySelector< HTMLButtonElement >(
72
+ '[role="combobox"]'
73
+ )
74
+ ?.focus();
75
+ } }
76
+ >
77
+ <option value="">No selection</option>
78
+ <option value="hasvalue">Has selection</option>
79
+ </select>
80
+ </div>
81
+ );
82
+ };
83
+
84
+ export const ValidatedCustomSelectControl = forwardRef(
85
+ UnforwardedValidatedCustomSelectControl
86
+ );
@@ -0,0 +1,12 @@
1
+ export * from './checkbox-control';
2
+ export * from './combobox-control';
3
+ export * from './custom-select-control';
4
+ export * from './input-control';
5
+ export * from './number-control';
6
+ export * from './radio-control';
7
+ export * from './range-control';
8
+ export * from './select-control';
9
+ export * from './text-control';
10
+ export * from './textarea-control';
11
+ export * from './toggle-control';
12
+ export * from './toggle-group-control';
@@ -0,0 +1,59 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { forwardRef, useRef } from '@wordpress/element';
5
+ import { useMergeRefs } from '@wordpress/compose';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { ControlWithError } from '../control-with-error';
11
+ import type { ValidatedControlProps } from './types';
12
+ import InputControl from '../../input-control';
13
+ import type { InputControlProps } from '../../input-control/types';
14
+
15
+ type Value = InputControlProps[ 'value' ];
16
+
17
+ const UnforwardedValidatedInputControl = (
18
+ {
19
+ required,
20
+ customValidator,
21
+ onChange,
22
+ markWhenOptional,
23
+ ...restProps
24
+ }: Omit<
25
+ React.ComponentProps< typeof InputControl >,
26
+ '__next40pxDefaultSize'
27
+ > &
28
+ ValidatedControlProps< InputControlProps[ 'value' ] >,
29
+ forwardedRef: React.ForwardedRef< HTMLInputElement >
30
+ ) => {
31
+ const validityTargetRef = useRef< HTMLInputElement >( null );
32
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
33
+ const valueRef = useRef< Value >( restProps.value );
34
+
35
+ return (
36
+ <ControlWithError
37
+ required={ required }
38
+ markWhenOptional={ markWhenOptional }
39
+ customValidator={ () => {
40
+ return customValidator?.( valueRef.current );
41
+ } }
42
+ getValidityTarget={ () => validityTargetRef.current }
43
+ >
44
+ <InputControl
45
+ __next40pxDefaultSize
46
+ ref={ mergedRefs }
47
+ onChange={ ( value, ...args ) => {
48
+ valueRef.current = value;
49
+ onChange?.( value, ...args );
50
+ } }
51
+ { ...restProps }
52
+ />
53
+ </ControlWithError>
54
+ );
55
+ };
56
+
57
+ export const ValidatedInputControl = forwardRef(
58
+ UnforwardedValidatedInputControl
59
+ );
@@ -0,0 +1,61 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { forwardRef, useRef } from '@wordpress/element';
5
+ import { useMergeRefs } from '@wordpress/compose';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { ControlWithError } from '../control-with-error';
11
+ import NumberControl from '../../number-control';
12
+ import type { ValidatedControlProps } from './types';
13
+ import type { NumberControlProps } from '../../number-control/types';
14
+
15
+ type Value = NumberControlProps[ 'value' ];
16
+
17
+ const UnforwardedValidatedNumberControl = (
18
+ {
19
+ required,
20
+ customValidator,
21
+ onChange,
22
+ markWhenOptional,
23
+ ...restProps
24
+ }: Omit<
25
+ React.ComponentProps< typeof NumberControl >,
26
+ '__next40pxDefaultSize'
27
+ > &
28
+ ValidatedControlProps< Value >,
29
+ forwardedRef: React.ForwardedRef< HTMLInputElement >
30
+ ) => {
31
+ const validityTargetRef = useRef< HTMLInputElement >( null );
32
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
33
+ const valueRef = useRef< Value >( restProps.value );
34
+
35
+ return (
36
+ <ControlWithError
37
+ required={ required }
38
+ markWhenOptional={ markWhenOptional }
39
+ customValidator={ () => {
40
+ return customValidator?.( valueRef.current );
41
+ } }
42
+ getValidityTarget={ () => validityTargetRef.current }
43
+ >
44
+ <NumberControl
45
+ __next40pxDefaultSize
46
+ ref={ mergedRefs }
47
+ // TODO: Upstream limitation - When form is submitted when value is undefined, it will
48
+ // automatically set a clamped value (as defined by `min` attribute, so 0 by default).
49
+ onChange={ ( value, ...args ) => {
50
+ valueRef.current = value;
51
+ onChange?.( value, ...args );
52
+ } }
53
+ { ...restProps }
54
+ />
55
+ </ControlWithError>
56
+ );
57
+ };
58
+
59
+ export const ValidatedNumberControl = forwardRef(
60
+ UnforwardedValidatedNumberControl
61
+ );
@@ -0,0 +1,60 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useMergeRefs } from '@wordpress/compose';
5
+ import { forwardRef, useRef } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { ControlWithError } from '../control-with-error';
11
+ import type { ValidatedControlProps } from './types';
12
+ import RadioControl from '../../radio-control';
13
+ import type { RadioControlProps } from '../../radio-control/types';
14
+
15
+ type Value = RadioControlProps[ 'selected' ];
16
+
17
+ const UnforwardedValidatedRadioControl = (
18
+ {
19
+ required,
20
+ customValidator,
21
+ onChange,
22
+ markWhenOptional,
23
+ ...restProps
24
+ }: React.ComponentProps< typeof RadioControl > &
25
+ ValidatedControlProps< Value >,
26
+ forwardedRef: React.ForwardedRef< HTMLDivElement >
27
+ ) => {
28
+ const validityTargetRef = useRef< HTMLDivElement >( null );
29
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
30
+ const valueRef = useRef< Value >( restProps.selected );
31
+
32
+ return (
33
+ <ControlWithError
34
+ required={ required }
35
+ markWhenOptional={ markWhenOptional }
36
+ // TODO: Upstream limitation - RadioControl does not accept a ref.
37
+ ref={ mergedRefs }
38
+ customValidator={ () => {
39
+ return customValidator?.( valueRef.current );
40
+ } }
41
+ getValidityTarget={ () =>
42
+ validityTargetRef.current?.querySelector< HTMLInputElement >(
43
+ 'input[type="radio"]'
44
+ )
45
+ }
46
+ >
47
+ <RadioControl
48
+ onChange={ ( value ) => {
49
+ valueRef.current = value;
50
+ onChange?.( value );
51
+ } }
52
+ { ...restProps }
53
+ />
54
+ </ControlWithError>
55
+ );
56
+ };
57
+
58
+ export const ValidatedRadioControl = forwardRef(
59
+ UnforwardedValidatedRadioControl
60
+ );
@@ -0,0 +1,60 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useMergeRefs } from '@wordpress/compose';
5
+ import { forwardRef, useRef } from '@wordpress/element';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { ControlWithError } from '../control-with-error';
11
+ import type { ValidatedControlProps } from './types';
12
+ import RangeControl from '../../range-control';
13
+ import type { RangeControlProps } from '../../range-control/types';
14
+
15
+ type Value = RangeControlProps[ 'value' ];
16
+
17
+ const UnforwardedValidatedRangeControl = (
18
+ {
19
+ required,
20
+ customValidator,
21
+ onChange,
22
+ markWhenOptional,
23
+ ...restProps
24
+ }: Omit<
25
+ React.ComponentProps< typeof RangeControl >,
26
+ '__next40pxDefaultSize' | '__nextHasNoMarginBottom'
27
+ > &
28
+ ValidatedControlProps< Value >,
29
+ forwardedRef: React.ForwardedRef< HTMLInputElement >
30
+ ) => {
31
+ const validityTargetRef = useRef< HTMLInputElement >( null );
32
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
33
+ const valueRef = useRef< Value >( restProps.value );
34
+
35
+ return (
36
+ <ControlWithError
37
+ required={ required }
38
+ markWhenOptional={ markWhenOptional }
39
+ customValidator={ () => {
40
+ return customValidator?.( valueRef.current );
41
+ } }
42
+ getValidityTarget={ () => validityTargetRef.current }
43
+ >
44
+ <RangeControl
45
+ __next40pxDefaultSize
46
+ __nextHasNoMarginBottom
47
+ ref={ mergedRefs }
48
+ onChange={ ( value ) => {
49
+ valueRef.current = value;
50
+ onChange?.( value );
51
+ } }
52
+ { ...restProps }
53
+ />
54
+ </ControlWithError>
55
+ );
56
+ };
57
+
58
+ export const ValidatedRangeControl = forwardRef(
59
+ UnforwardedValidatedRangeControl
60
+ );
@@ -0,0 +1,75 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { forwardRef, useRef } from '@wordpress/element';
5
+ import { useMergeRefs } from '@wordpress/compose';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { ControlWithError } from '../control-with-error';
11
+ import SelectControl from '../../select-control';
12
+ import type { ValidatedControlProps } from './types';
13
+ import type { SelectControlProps as _SelectControlProps } from '../../select-control/types';
14
+
15
+ // Only support single value selection
16
+ type SelectControlProps = Omit<
17
+ _SelectControlProps,
18
+ 'multiple' | 'onChange' | 'value'
19
+ > & {
20
+ onChange?: ( value: string ) => void;
21
+ value?: string;
22
+ };
23
+
24
+ type Value = SelectControlProps[ 'value' ];
25
+
26
+ const UnforwardedValidatedSelectControl = (
27
+ {
28
+ required,
29
+ customValidator,
30
+ onChange,
31
+ markWhenOptional,
32
+ ...restProps
33
+ }: Omit<
34
+ React.ComponentProps< typeof SelectControl >,
35
+ | '__next40pxDefaultSize'
36
+ | '__nextHasNoMarginBottom'
37
+ | 'multiple'
38
+ | 'onChange'
39
+ | 'value'
40
+ > & {
41
+ value?: string;
42
+ onChange: ( value: string ) => void;
43
+ } & ValidatedControlProps< Value >,
44
+ forwardedRef: React.ForwardedRef< HTMLSelectElement >
45
+ ) => {
46
+ const validityTargetRef = useRef< HTMLSelectElement >( null );
47
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
48
+ const valueRef = useRef< Value >( restProps.value );
49
+
50
+ return (
51
+ <ControlWithError
52
+ required={ required }
53
+ markWhenOptional={ markWhenOptional }
54
+ customValidator={ () => {
55
+ return customValidator?.( valueRef.current );
56
+ } }
57
+ getValidityTarget={ () => validityTargetRef.current }
58
+ >
59
+ <SelectControl
60
+ __nextHasNoMarginBottom
61
+ __next40pxDefaultSize
62
+ ref={ mergedRefs }
63
+ onChange={ ( value ) => {
64
+ valueRef.current = value;
65
+ onChange?.( value );
66
+ } }
67
+ { ...restProps }
68
+ />
69
+ </ControlWithError>
70
+ );
71
+ };
72
+
73
+ export const ValidatedSelectControl = forwardRef(
74
+ UnforwardedValidatedSelectControl
75
+ );
@@ -0,0 +1,57 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useState } from '@wordpress/element';
5
+
6
+ /**
7
+ * External dependencies
8
+ */
9
+ import type { StoryObj, Meta } from '@storybook/react';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { ValidatedCheckboxControl } from '../checkbox-control';
15
+ import { formDecorator } from './story-utils';
16
+
17
+ const meta: Meta< typeof ValidatedCheckboxControl > = {
18
+ title: 'Components (Experimental)/Validated Form Controls/ValidatedCheckboxControl',
19
+ component: ValidatedCheckboxControl,
20
+ tags: [ 'status-private' ],
21
+ decorators: formDecorator,
22
+ args: { onChange: () => {} },
23
+ argTypes: {
24
+ checked: { control: false },
25
+ // TODO: Figure out why this deprecated prop is still showing up here and not in the WP Storybook.
26
+ heading: { table: { disable: true } },
27
+ },
28
+ };
29
+ export default meta;
30
+
31
+ export const Default: StoryObj< typeof ValidatedCheckboxControl > = {
32
+ render: function Template( { onChange, ...args } ) {
33
+ const [ checked, setChecked ] = useState( false );
34
+
35
+ return (
36
+ <ValidatedCheckboxControl
37
+ { ...args }
38
+ checked={ checked }
39
+ onChange={ ( value ) => {
40
+ setChecked( value );
41
+ onChange?.( value );
42
+ } }
43
+ />
44
+ );
45
+ },
46
+ };
47
+ Default.args = {
48
+ required: true,
49
+ label: 'Checkbox',
50
+ help: 'This checkbox may neither be checked nor unchecked.',
51
+ customValidator: ( value ) => {
52
+ if ( value ) {
53
+ return 'This checkbox may not be checked.';
54
+ }
55
+ return undefined;
56
+ },
57
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useState } from '@wordpress/element';
5
+
6
+ /**
7
+ * External dependencies
8
+ */
9
+ import type { StoryObj, Meta } from '@storybook/react';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { ValidatedComboboxControl } from '../combobox-control';
15
+ import { formDecorator } from './story-utils';
16
+
17
+ const meta: Meta< typeof ValidatedComboboxControl > = {
18
+ title: 'Components (Experimental)/Validated Form Controls/ValidatedComboboxControl',
19
+ component: ValidatedComboboxControl,
20
+ tags: [ 'status-private' ],
21
+ decorators: formDecorator,
22
+ args: { onChange: () => {} },
23
+ argTypes: {
24
+ value: { control: false },
25
+ },
26
+ };
27
+ export default meta;
28
+
29
+ export const Default: StoryObj< typeof ValidatedComboboxControl > = {
30
+ render: function Template( { onChange, ...args } ) {
31
+ const [ value, setValue ] =
32
+ useState<
33
+ React.ComponentProps<
34
+ typeof ValidatedComboboxControl
35
+ >[ 'value' ]
36
+ >();
37
+
38
+ return (
39
+ <ValidatedComboboxControl
40
+ { ...args }
41
+ value={ value }
42
+ onChange={ ( newValue ) => {
43
+ setValue( newValue );
44
+ onChange?.( newValue );
45
+ } }
46
+ />
47
+ );
48
+ },
49
+ };
50
+ Default.args = {
51
+ required: true,
52
+ label: 'Combobox',
53
+ help: 'Option A is not allowed.',
54
+ options: [
55
+ { value: 'a', label: 'Option A (not allowed)' },
56
+ { value: 'b', label: 'Option B' },
57
+ ],
58
+ customValidator: ( value ) => {
59
+ if ( value === 'a' ) {
60
+ return 'Option A is not allowed.';
61
+ }
62
+ return undefined;
63
+ },
64
+ };
@@ -0,0 +1,64 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useState } from '@wordpress/element';
5
+
6
+ /**
7
+ * External dependencies
8
+ */
9
+ import type { StoryObj, Meta } from '@storybook/react';
10
+
11
+ /**
12
+ * Internal dependencies
13
+ */
14
+ import { ValidatedCustomSelectControl } from '../custom-select-control';
15
+ import { formDecorator } from './story-utils';
16
+
17
+ const meta: Meta< typeof ValidatedCustomSelectControl > = {
18
+ title: 'Components (Experimental)/Validated Form Controls/ValidatedCustomSelectControl',
19
+ component: ValidatedCustomSelectControl,
20
+ tags: [ 'status-private' ],
21
+ decorators: formDecorator,
22
+ args: { onChange: () => {} },
23
+ argTypes: {
24
+ value: { control: false },
25
+ },
26
+ };
27
+ export default meta;
28
+
29
+ export const Default: StoryObj< typeof ValidatedCustomSelectControl > = {
30
+ render: function Template( { onChange, ...args } ) {
31
+ const [ value, setValue ] =
32
+ useState<
33
+ React.ComponentProps<
34
+ typeof ValidatedCustomSelectControl
35
+ >[ 'value' ]
36
+ >();
37
+
38
+ return (
39
+ <ValidatedCustomSelectControl
40
+ { ...args }
41
+ value={ value }
42
+ onChange={ ( newValue ) => {
43
+ setValue( newValue.selectedItem );
44
+ onChange?.( newValue );
45
+ } }
46
+ />
47
+ );
48
+ },
49
+ };
50
+ Default.args = {
51
+ required: true,
52
+ label: 'Custom Select',
53
+ options: [
54
+ { key: '', name: 'Select an option' },
55
+ { key: 'a', name: 'Option A (not allowed)' },
56
+ { key: 'b', name: 'Option B' },
57
+ ],
58
+ customValidator: ( value ) => {
59
+ if ( value?.key === 'a' ) {
60
+ return 'Option A is not allowed.';
61
+ }
62
+ return undefined;
63
+ },
64
+ };