@wordpress/components 29.12.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 (337) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/build/autocomplete/index.js +4 -0
  3. package/build/autocomplete/index.js.map +1 -1
  4. package/build/box-control/input-control.js +2 -2
  5. package/build/box-control/input-control.js.map +1 -1
  6. package/build/calendar/date-calendar/index.js +69 -0
  7. package/build/calendar/date-calendar/index.js.map +1 -0
  8. package/build/calendar/date-range-calendar/index.js +172 -0
  9. package/build/calendar/date-range-calendar/index.js.map +1 -0
  10. package/build/calendar/index.js +27 -0
  11. package/build/calendar/index.js.map +1 -0
  12. package/build/calendar/types.js +6 -0
  13. package/build/calendar/types.js.map +1 -0
  14. package/build/calendar/utils/constants.js +68 -0
  15. package/build/calendar/utils/constants.js.map +1 -0
  16. package/build/calendar/utils/day-cell.js +137 -0
  17. package/build/calendar/utils/day-cell.js.map +1 -0
  18. package/build/calendar/utils/misc.js +10 -0
  19. package/build/calendar/utils/misc.js.map +1 -0
  20. package/build/calendar/utils/use-localization-props.js +162 -0
  21. package/build/calendar/utils/use-localization-props.js.map +1 -0
  22. package/build/custom-gradient-picker/gradient-bar/control-points.js +1 -1
  23. package/build/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  24. package/build/custom-select-control-v2/custom-select.js +3 -3
  25. package/build/custom-select-control-v2/custom-select.js.map +1 -1
  26. package/build/date-time/date/index.js +1 -1
  27. package/build/date-time/date/index.js.map +1 -1
  28. package/build/form-token-field/index.js +11 -1
  29. package/build/form-token-field/index.js.map +1 -1
  30. package/build/form-token-field/token.js +1 -1
  31. package/build/form-token-field/token.js.map +1 -1
  32. package/build/icon/index.js +2 -0
  33. package/build/icon/index.js.map +1 -1
  34. package/build/mobile/bottom-sheet/cell.native.js +2 -2
  35. package/build/mobile/bottom-sheet/cell.native.js.map +1 -1
  36. package/build/mobile/link-picker/index.native.js +1 -1
  37. package/build/mobile/link-picker/index.native.js.map +1 -1
  38. package/build/navigation/menu/menu-title-search.js +1 -1
  39. package/build/navigation/menu/menu-title-search.js.map +1 -1
  40. package/build/palette-edit/index.js +4 -4
  41. package/build/palette-edit/index.js.map +1 -1
  42. package/build/private-apis.js +5 -1
  43. package/build/private-apis.js.map +1 -1
  44. package/build/select-control/index.js +1 -1
  45. package/build/select-control/index.js.map +1 -1
  46. package/build/toggle-group-control/toggle-group-control/as-button-group.js.map +1 -1
  47. package/build/utils/hooks/use-controlled-value.js +8 -4
  48. package/build/utils/hooks/use-controlled-value.js.map +1 -1
  49. package/build/validated-form-controls/components/checkbox-control.js +52 -0
  50. package/build/validated-form-controls/components/checkbox-control.js.map +1 -0
  51. package/build/validated-form-controls/components/combobox-control.js +64 -0
  52. package/build/validated-form-controls/components/combobox-control.js.map +1 -0
  53. package/build/validated-form-controls/components/custom-select-control.js +71 -0
  54. package/build/validated-form-controls/components/custom-select-control.js.map +1 -0
  55. package/build/validated-form-controls/components/index.js +138 -0
  56. package/build/validated-form-controls/components/index.js.map +1 -0
  57. package/build/validated-form-controls/components/input-control.js +50 -0
  58. package/build/validated-form-controls/components/input-control.js.map +1 -0
  59. package/build/validated-form-controls/components/number-control.js +53 -0
  60. package/build/validated-form-controls/components/number-control.js.map +1 -0
  61. package/build/validated-form-controls/components/radio-control.js +51 -0
  62. package/build/validated-form-controls/components/radio-control.js.map +1 -0
  63. package/build/validated-form-controls/components/range-control.js +51 -0
  64. package/build/validated-form-controls/components/range-control.js.map +1 -0
  65. package/build/validated-form-controls/components/select-control.js +53 -0
  66. package/build/validated-form-controls/components/select-control.js.map +1 -0
  67. package/build/validated-form-controls/components/text-control.js +51 -0
  68. package/build/validated-form-controls/components/text-control.js.map +1 -0
  69. package/build/validated-form-controls/components/textarea-control.js +50 -0
  70. package/build/validated-form-controls/components/textarea-control.js.map +1 -0
  71. package/build/validated-form-controls/components/toggle-control.js +60 -0
  72. package/build/validated-form-controls/components/toggle-control.js.map +1 -0
  73. package/build/validated-form-controls/components/toggle-group-control.js +69 -0
  74. package/build/validated-form-controls/components/toggle-group-control.js.map +1 -0
  75. package/build/validated-form-controls/components/types.js +6 -0
  76. package/build/validated-form-controls/components/types.js.map +1 -0
  77. package/build/validated-form-controls/control-with-error.js +137 -0
  78. package/build/validated-form-controls/control-with-error.js.map +1 -0
  79. package/build/validated-form-controls/index.js +28 -0
  80. package/build/validated-form-controls/index.js.map +1 -0
  81. package/build-module/autocomplete/index.js +4 -0
  82. package/build-module/autocomplete/index.js.map +1 -1
  83. package/build-module/box-control/input-control.js +2 -2
  84. package/build-module/box-control/input-control.js.map +1 -1
  85. package/build-module/calendar/date-calendar/index.js +59 -0
  86. package/build-module/calendar/date-calendar/index.js.map +1 -0
  87. package/build-module/calendar/date-range-calendar/index.js +161 -0
  88. package/build-module/calendar/date-range-calendar/index.js.map +1 -0
  89. package/build-module/calendar/index.js +4 -0
  90. package/build-module/calendar/index.js.map +1 -0
  91. package/build-module/calendar/types.js +2 -0
  92. package/build-module/calendar/types.js.map +1 -0
  93. package/build-module/calendar/utils/constants.js +61 -0
  94. package/build-module/calendar/utils/constants.js.map +1 -0
  95. package/build-module/calendar/utils/day-cell.js +131 -0
  96. package/build-module/calendar/utils/day-cell.js.map +1 -0
  97. package/build-module/calendar/utils/misc.js +4 -0
  98. package/build-module/calendar/utils/misc.js.map +1 -0
  99. package/build-module/calendar/utils/use-localization-props.js +154 -0
  100. package/build-module/calendar/utils/use-localization-props.js.map +1 -0
  101. package/build-module/custom-gradient-picker/gradient-bar/control-points.js +1 -1
  102. package/build-module/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  103. package/build-module/custom-select-control-v2/custom-select.js +4 -4
  104. package/build-module/custom-select-control-v2/custom-select.js.map +1 -1
  105. package/build-module/date-time/date/index.js +1 -1
  106. package/build-module/date-time/date/index.js.map +1 -1
  107. package/build-module/form-token-field/index.js +11 -1
  108. package/build-module/form-token-field/index.js.map +1 -1
  109. package/build-module/form-token-field/token.js +1 -1
  110. package/build-module/form-token-field/token.js.map +1 -1
  111. package/build-module/icon/index.js +2 -0
  112. package/build-module/icon/index.js.map +1 -1
  113. package/build-module/mobile/bottom-sheet/cell.native.js +2 -2
  114. package/build-module/mobile/bottom-sheet/cell.native.js.map +1 -1
  115. package/build-module/mobile/link-picker/index.native.js +1 -1
  116. package/build-module/mobile/link-picker/index.native.js.map +1 -1
  117. package/build-module/navigation/menu/menu-title-search.js +1 -1
  118. package/build-module/navigation/menu/menu-title-search.js.map +1 -1
  119. package/build-module/palette-edit/index.js +4 -4
  120. package/build-module/palette-edit/index.js.map +1 -1
  121. package/build-module/private-apis.js +5 -1
  122. package/build-module/private-apis.js.map +1 -1
  123. package/build-module/select-control/index.js +1 -1
  124. package/build-module/select-control/index.js.map +1 -1
  125. package/build-module/toggle-group-control/toggle-group-control/as-button-group.js.map +1 -1
  126. package/build-module/utils/hooks/use-controlled-value.js +9 -5
  127. package/build-module/utils/hooks/use-controlled-value.js.map +1 -1
  128. package/build-module/validated-form-controls/components/checkbox-control.js +44 -0
  129. package/build-module/validated-form-controls/components/checkbox-control.js.map +1 -0
  130. package/build-module/validated-form-controls/components/combobox-control.js +56 -0
  131. package/build-module/validated-form-controls/components/combobox-control.js.map +1 -0
  132. package/build-module/validated-form-controls/components/custom-select-control.js +63 -0
  133. package/build-module/validated-form-controls/components/custom-select-control.js.map +1 -0
  134. package/build-module/validated-form-controls/components/index.js +13 -0
  135. package/build-module/validated-form-controls/components/index.js.map +1 -0
  136. package/build-module/validated-form-controls/components/input-control.js +42 -0
  137. package/build-module/validated-form-controls/components/input-control.js.map +1 -0
  138. package/build-module/validated-form-controls/components/number-control.js +45 -0
  139. package/build-module/validated-form-controls/components/number-control.js.map +1 -0
  140. package/build-module/validated-form-controls/components/radio-control.js +43 -0
  141. package/build-module/validated-form-controls/components/radio-control.js.map +1 -0
  142. package/build-module/validated-form-controls/components/range-control.js +43 -0
  143. package/build-module/validated-form-controls/components/range-control.js.map +1 -0
  144. package/build-module/validated-form-controls/components/select-control.js +45 -0
  145. package/build-module/validated-form-controls/components/select-control.js.map +1 -0
  146. package/build-module/validated-form-controls/components/text-control.js +43 -0
  147. package/build-module/validated-form-controls/components/text-control.js.map +1 -0
  148. package/build-module/validated-form-controls/components/textarea-control.js +42 -0
  149. package/build-module/validated-form-controls/components/textarea-control.js.map +1 -0
  150. package/build-module/validated-form-controls/components/toggle-control.js +52 -0
  151. package/build-module/validated-form-controls/components/toggle-control.js.map +1 -0
  152. package/build-module/validated-form-controls/components/toggle-group-control.js +62 -0
  153. package/build-module/validated-form-controls/components/toggle-group-control.js.map +1 -0
  154. package/build-module/validated-form-controls/components/types.js +2 -0
  155. package/build-module/validated-form-controls/components/types.js.map +1 -0
  156. package/build-module/validated-form-controls/control-with-error.js +129 -0
  157. package/build-module/validated-form-controls/control-with-error.js.map +1 -0
  158. package/build-module/validated-form-controls/index.js +3 -0
  159. package/build-module/validated-form-controls/index.js.map +1 -0
  160. package/build-style/style-rtl.css +418 -22
  161. package/build-style/style.css +418 -22
  162. package/build-types/autocomplete/index.d.ts.map +1 -1
  163. package/build-types/box-control/input-control.d.ts.map +1 -1
  164. package/build-types/box-control/utils.d.ts +7 -7
  165. package/build-types/calendar/date-calendar/index.d.ts +11 -0
  166. package/build-types/calendar/date-calendar/index.d.ts.map +1 -0
  167. package/build-types/calendar/date-range-calendar/index.d.ts +14 -0
  168. package/build-types/calendar/date-range-calendar/index.d.ts.map +1 -0
  169. package/build-types/calendar/index.d.ts +4 -0
  170. package/build-types/calendar/index.d.ts.map +1 -0
  171. package/build-types/calendar/stories/date-calendar.story.d.ts +16 -0
  172. package/build-types/calendar/stories/date-calendar.story.d.ts.map +1 -0
  173. package/build-types/calendar/stories/date-range-calendar.story.d.ts +16 -0
  174. package/build-types/calendar/stories/date-range-calendar.story.d.ts.map +1 -0
  175. package/build-types/calendar/test/__utils__/index.d.ts +10 -0
  176. package/build-types/calendar/test/__utils__/index.d.ts.map +1 -0
  177. package/build-types/calendar/test/date-calendar.d.ts +2 -0
  178. package/build-types/calendar/test/date-calendar.d.ts.map +1 -0
  179. package/build-types/calendar/test/date-range-calendar.d.ts +2 -0
  180. package/build-types/calendar/test/date-range-calendar.d.ts.map +1 -0
  181. package/build-types/calendar/types.d.ts +317 -0
  182. package/build-types/calendar/types.d.ts.map +1 -0
  183. package/build-types/calendar/utils/constants.d.ts +52 -0
  184. package/build-types/calendar/utils/constants.d.ts.map +1 -0
  185. package/build-types/calendar/utils/day-cell.d.ts +21 -0
  186. package/build-types/calendar/utils/day-cell.d.ts.map +1 -0
  187. package/build-types/calendar/utils/misc.d.ts +2 -0
  188. package/build-types/calendar/utils/misc.d.ts.map +1 -0
  189. package/build-types/calendar/utils/use-localization-props.d.ts +64 -0
  190. package/build-types/calendar/utils/use-localization-props.d.ts.map +1 -0
  191. package/build-types/color-picker/styles.d.ts.map +1 -1
  192. package/build-types/custom-gradient-picker/constants.d.ts +6 -3
  193. package/build-types/custom-gradient-picker/constants.d.ts.map +1 -1
  194. package/build-types/custom-select-control-v2/custom-select.d.ts.map +1 -1
  195. package/build-types/dimension-control/sizes.d.ts +15 -3
  196. package/build-types/dimension-control/sizes.d.ts.map +1 -1
  197. package/build-types/font-size-picker/constants.d.ts +2 -2
  198. package/build-types/font-size-picker/constants.d.ts.map +1 -1
  199. package/build-types/form-token-field/index.d.ts.map +1 -1
  200. package/build-types/icon/index.d.ts.map +1 -1
  201. package/build-types/popover/overlay-middlewares.d.ts +6 -1
  202. package/build-types/popover/overlay-middlewares.d.ts.map +1 -1
  203. package/build-types/private-apis.d.ts.map +1 -1
  204. package/build-types/select-control/stories/index.story.d.ts.map +1 -1
  205. package/build-types/toggle-group-control/toggle-group-control/as-button-group.d.ts.map +1 -1
  206. package/build-types/utils/hooks/use-controlled-value.d.ts +2 -2
  207. package/build-types/utils/hooks/use-controlled-value.d.ts.map +1 -1
  208. package/build-types/validated-form-controls/components/checkbox-control.d.ts +9 -0
  209. package/build-types/validated-form-controls/components/checkbox-control.d.ts.map +1 -0
  210. package/build-types/validated-form-controls/components/combobox-control.d.ts +21 -0
  211. package/build-types/validated-form-controls/components/combobox-control.d.ts.map +1 -0
  212. package/build-types/validated-form-controls/components/custom-select-control.d.ts +4 -0
  213. package/build-types/validated-form-controls/components/custom-select-control.d.ts.map +1 -0
  214. package/build-types/validated-form-controls/components/index.d.ts +13 -0
  215. package/build-types/validated-form-controls/components/index.d.ts.map +1 -0
  216. package/build-types/validated-form-controls/components/input-control.d.ts +4 -0
  217. package/build-types/validated-form-controls/components/input-control.d.ts.map +1 -0
  218. package/build-types/validated-form-controls/components/number-control.d.ts +17 -0
  219. package/build-types/validated-form-controls/components/number-control.d.ts.map +1 -0
  220. package/build-types/validated-form-controls/components/radio-control.d.ts +11 -0
  221. package/build-types/validated-form-controls/components/radio-control.d.ts.map +1 -0
  222. package/build-types/validated-form-controls/components/range-control.d.ts +36 -0
  223. package/build-types/validated-form-controls/components/range-control.d.ts.map +1 -0
  224. package/build-types/validated-form-controls/components/select-control.d.ts +9 -0
  225. package/build-types/validated-form-controls/components/select-control.d.ts.map +1 -0
  226. package/build-types/validated-form-controls/components/stories/checkbox-control.story.d.ts +12 -0
  227. package/build-types/validated-form-controls/components/stories/checkbox-control.story.d.ts.map +1 -0
  228. package/build-types/validated-form-controls/components/stories/combobox-control.story.d.ts +12 -0
  229. package/build-types/validated-form-controls/components/stories/combobox-control.story.d.ts.map +1 -0
  230. package/build-types/validated-form-controls/components/stories/custom-select-control.story.d.ts +12 -0
  231. package/build-types/validated-form-controls/components/stories/custom-select-control.story.d.ts.map +1 -0
  232. package/build-types/validated-form-controls/components/stories/input-control.story.d.ts +18 -0
  233. package/build-types/validated-form-controls/components/stories/input-control.story.d.ts.map +1 -0
  234. package/build-types/validated-form-controls/components/stories/number-control.story.d.ts +12 -0
  235. package/build-types/validated-form-controls/components/stories/number-control.story.d.ts.map +1 -0
  236. package/build-types/validated-form-controls/components/stories/overview.story.d.ts +19 -0
  237. package/build-types/validated-form-controls/components/stories/overview.story.d.ts.map +1 -0
  238. package/build-types/validated-form-controls/components/stories/radio-control.story.d.ts +12 -0
  239. package/build-types/validated-form-controls/components/stories/radio-control.story.d.ts.map +1 -0
  240. package/build-types/validated-form-controls/components/stories/range-control.story.d.ts +9 -0
  241. package/build-types/validated-form-controls/components/stories/range-control.story.d.ts.map +1 -0
  242. package/build-types/validated-form-controls/components/stories/select-control.story.d.ts +12 -0
  243. package/build-types/validated-form-controls/components/stories/select-control.story.d.ts.map +1 -0
  244. package/build-types/validated-form-controls/components/stories/story-utils.d.ts +9 -0
  245. package/build-types/validated-form-controls/components/stories/story-utils.d.ts.map +1 -0
  246. package/build-types/validated-form-controls/components/stories/text-control.story.d.ts +9 -0
  247. package/build-types/validated-form-controls/components/stories/text-control.story.d.ts.map +1 -0
  248. package/build-types/validated-form-controls/components/stories/textarea-control.story.d.ts +9 -0
  249. package/build-types/validated-form-controls/components/stories/textarea-control.story.d.ts.map +1 -0
  250. package/build-types/validated-form-controls/components/stories/toggle-control.story.d.ts +9 -0
  251. package/build-types/validated-form-controls/components/stories/toggle-control.story.d.ts.map +1 -0
  252. package/build-types/validated-form-controls/components/stories/toggle-group-control.story.d.ts +9 -0
  253. package/build-types/validated-form-controls/components/stories/toggle-group-control.story.d.ts.map +1 -0
  254. package/build-types/validated-form-controls/components/text-control.d.ts +8 -0
  255. package/build-types/validated-form-controls/components/text-control.d.ts.map +1 -0
  256. package/build-types/validated-form-controls/components/textarea-control.d.ts +7 -0
  257. package/build-types/validated-form-controls/components/textarea-control.d.ts.map +1 -0
  258. package/build-types/validated-form-controls/components/toggle-control.d.ts +7 -0
  259. package/build-types/validated-form-controls/components/toggle-control.d.ts.map +1 -0
  260. package/build-types/validated-form-controls/components/toggle-group-control.d.ts +15 -0
  261. package/build-types/validated-form-controls/components/toggle-group-control.d.ts.map +1 -0
  262. package/build-types/validated-form-controls/components/types.d.ts +27 -0
  263. package/build-types/validated-form-controls/components/types.d.ts.map +1 -0
  264. package/build-types/validated-form-controls/control-with-error.d.ts +36 -0
  265. package/build-types/validated-form-controls/control-with-error.d.ts.map +1 -0
  266. package/build-types/validated-form-controls/index.d.ts +3 -0
  267. package/build-types/validated-form-controls/index.d.ts.map +1 -0
  268. package/package.json +21 -20
  269. package/src/autocomplete/index.tsx +4 -0
  270. package/src/box-control/input-control.tsx +14 -5
  271. package/src/calendar/date-calendar/README.md +261 -0
  272. package/src/calendar/date-calendar/index.tsx +69 -0
  273. package/src/calendar/date-range-calendar/README.md +298 -0
  274. package/src/calendar/date-range-calendar/index.tsx +215 -0
  275. package/src/calendar/index.tsx +3 -0
  276. package/src/calendar/stories/date-calendar.story.tsx +221 -0
  277. package/src/calendar/stories/date-range-calendar.story.tsx +230 -0
  278. package/src/calendar/style.scss +431 -0
  279. package/src/calendar/test/__utils__/index.ts +56 -0
  280. package/src/calendar/test/date-calendar.tsx +975 -0
  281. package/src/calendar/test/date-range-calendar.tsx +1701 -0
  282. package/src/calendar/types.ts +342 -0
  283. package/src/calendar/utils/constants.ts +62 -0
  284. package/src/calendar/utils/day-cell.tsx +133 -0
  285. package/src/calendar/utils/misc.ts +3 -0
  286. package/src/calendar/utils/use-localization-props.ts +169 -0
  287. package/src/custom-gradient-picker/gradient-bar/control-points.tsx +1 -1
  288. package/src/custom-select-control-v2/custom-select.tsx +6 -3
  289. package/src/date-time/date/index.tsx +1 -1
  290. package/src/dimension-control/test/__snapshots__/index.test.js.snap +8 -8
  291. package/src/form-token-field/index.tsx +12 -1
  292. package/src/form-token-field/token.tsx +1 -1
  293. package/src/icon/index.tsx +2 -0
  294. package/src/mobile/bottom-sheet/cell.native.js +2 -2
  295. package/src/mobile/link-picker/index.native.js +1 -1
  296. package/src/navigation/menu/menu-title-search.tsx +1 -1
  297. package/src/palette-edit/index.tsx +4 -4
  298. package/src/private-apis.ts +5 -0
  299. package/src/select-control/index.tsx +1 -1
  300. package/src/select-control/style.scss +0 -6
  301. package/src/style.scss +3 -2
  302. package/src/toggle-group-control/toggle-group-control/as-button-group.tsx +3 -1
  303. package/src/utils/hooks/use-controlled-value.ts +16 -8
  304. package/src/validated-form-controls/components/checkbox-control.tsx +64 -0
  305. package/src/validated-form-controls/components/combobox-control.tsx +77 -0
  306. package/src/validated-form-controls/components/custom-select-control.tsx +86 -0
  307. package/src/validated-form-controls/components/index.ts +12 -0
  308. package/src/validated-form-controls/components/input-control.tsx +59 -0
  309. package/src/validated-form-controls/components/number-control.tsx +61 -0
  310. package/src/validated-form-controls/components/radio-control.tsx +60 -0
  311. package/src/validated-form-controls/components/range-control.tsx +60 -0
  312. package/src/validated-form-controls/components/select-control.tsx +75 -0
  313. package/src/validated-form-controls/components/stories/checkbox-control.story.tsx +57 -0
  314. package/src/validated-form-controls/components/stories/combobox-control.story.tsx +64 -0
  315. package/src/validated-form-controls/components/stories/custom-select-control.story.tsx +64 -0
  316. package/src/validated-form-controls/components/stories/input-control.story.tsx +132 -0
  317. package/src/validated-form-controls/components/stories/number-control.story.tsx +62 -0
  318. package/src/validated-form-controls/components/stories/overview.mdx +52 -0
  319. package/src/validated-form-controls/components/stories/overview.story.tsx +100 -0
  320. package/src/validated-form-controls/components/stories/radio-control.story.tsx +64 -0
  321. package/src/validated-form-controls/components/stories/range-control.story.tsx +60 -0
  322. package/src/validated-form-controls/components/stories/select-control.story.tsx +60 -0
  323. package/src/validated-form-controls/components/stories/story-utils.tsx +46 -0
  324. package/src/validated-form-controls/components/stories/text-control.story.tsx +55 -0
  325. package/src/validated-form-controls/components/stories/textarea-control.story.tsx +52 -0
  326. package/src/validated-form-controls/components/stories/toggle-control.story.tsx +55 -0
  327. package/src/validated-form-controls/components/stories/toggle-group-control.story.tsx +66 -0
  328. package/src/validated-form-controls/components/text-control.tsx +60 -0
  329. package/src/validated-form-controls/components/textarea-control.tsx +59 -0
  330. package/src/validated-form-controls/components/toggle-control.tsx +69 -0
  331. package/src/validated-form-controls/components/toggle-group-control.tsx +82 -0
  332. package/src/validated-form-controls/components/types.ts +28 -0
  333. package/src/validated-form-controls/control-with-error.tsx +198 -0
  334. package/src/validated-form-controls/index.ts +2 -0
  335. package/src/validated-form-controls/style.scss +75 -0
  336. package/tsconfig.tsbuildinfo +1 -1
  337. package/src/dimension-control/style.scss +0 -22
@@ -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
+ };
@@ -0,0 +1,132 @@
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
+ * WordPress dependencies
13
+ */
14
+ import { seen, unseen } from '@wordpress/icons';
15
+
16
+ /**
17
+ * Internal dependencies
18
+ */
19
+ import { ValidatedInputControl } from '../input-control';
20
+ import { formDecorator } from './story-utils';
21
+ import InputControlSuffixWrapper from '../../../input-control/input-suffix-wrapper';
22
+ import { Button } from '../../../button';
23
+
24
+ const meta: Meta< typeof ValidatedInputControl > = {
25
+ title: 'Components (Experimental)/Validated Form Controls/ValidatedInputControl',
26
+ component: ValidatedInputControl,
27
+ tags: [ 'status-private' ],
28
+ decorators: formDecorator,
29
+ args: { onChange: () => {} },
30
+ argTypes: {
31
+ __unstableInputWidth: { control: { type: 'text' } },
32
+ __unstableStateReducer: { control: false },
33
+ onChange: { control: false },
34
+ prefix: { control: false },
35
+ suffix: { control: false },
36
+ type: { control: { type: 'text' } },
37
+ value: { control: false },
38
+ },
39
+ };
40
+ export default meta;
41
+
42
+ export const Default: StoryObj< typeof ValidatedInputControl > = {
43
+ render: function Template( { onChange, ...args } ) {
44
+ const [ value, setValue ] =
45
+ useState<
46
+ React.ComponentProps< typeof ValidatedInputControl >[ 'value' ]
47
+ >( '' );
48
+
49
+ return (
50
+ <ValidatedInputControl
51
+ { ...args }
52
+ value={ value }
53
+ onChange={ ( newValue, ...rest ) => {
54
+ setValue( newValue );
55
+ onChange?.( newValue, ...rest );
56
+ } }
57
+ />
58
+ );
59
+ },
60
+ };
61
+ Default.args = {
62
+ required: true,
63
+ label: 'Input',
64
+ help: 'The word "error" will trigger an error.',
65
+ customValidator: ( value ) => {
66
+ if ( value?.toLowerCase() === 'error' ) {
67
+ return 'The word "error" is not allowed.';
68
+ }
69
+ return undefined;
70
+ },
71
+ };
72
+
73
+ /**
74
+ * This demonstrates how password validation would work with the standard implementation.
75
+ *
76
+ * We are planning to move to a custom implementation more tailored to the password use case.
77
+ */
78
+ export const Password: StoryObj< typeof ValidatedInputControl > = {
79
+ render: function Template( { onChange, ...args } ) {
80
+ const [ value, setValue ] =
81
+ useState<
82
+ React.ComponentProps< typeof ValidatedInputControl >[ 'value' ]
83
+ >( '' );
84
+ const [ visible, setVisible ] = useState( false );
85
+
86
+ return (
87
+ <ValidatedInputControl
88
+ { ...args }
89
+ type={ visible ? 'text' : 'password' }
90
+ suffix={
91
+ <InputControlSuffixWrapper variant="control">
92
+ <Button
93
+ size="small"
94
+ icon={ visible ? unseen : seen }
95
+ onClick={ () => setVisible( ( v ) => ! v ) }
96
+ label={
97
+ visible ? 'Hide password' : 'Show password'
98
+ }
99
+ />
100
+ </InputControlSuffixWrapper>
101
+ }
102
+ value={ value }
103
+ onChange={ ( newValue, ...rest ) => {
104
+ setValue( newValue );
105
+ onChange?.( newValue, ...rest );
106
+ } }
107
+ />
108
+ );
109
+ },
110
+ };
111
+ Password.args = {
112
+ required: true,
113
+ label: 'Password',
114
+ help: 'Minimum 8 characters, include a number, capital letter, and symbol (!@£$%^&*#).',
115
+ minLength: 8,
116
+ customValidator: ( value ) => {
117
+ if ( ! /\d/.test( value ?? '' ) ) {
118
+ return 'Password must include at least one number.';
119
+ }
120
+ if ( ! /[A-Z]/.test( value ?? '' ) ) {
121
+ return 'Password must include at least one capital letter.';
122
+ }
123
+ if ( ! /[!@£$%^&*#]/.test( value ?? '' ) ) {
124
+ return 'Password must include at least one symbol.';
125
+ }
126
+ return undefined;
127
+ },
128
+ };
129
+ Password.argTypes = {
130
+ suffix: { control: false },
131
+ type: { control: false },
132
+ };
@@ -0,0 +1,62 @@
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 { ValidatedNumberControl } from '../number-control';
15
+ import { formDecorator } from './story-utils';
16
+
17
+ const meta: Meta< typeof ValidatedNumberControl > = {
18
+ title: 'Components (Experimental)/Validated Form Controls/ValidatedNumberControl',
19
+ component: ValidatedNumberControl,
20
+ tags: [ 'status-private' ],
21
+ decorators: formDecorator,
22
+ args: { onChange: () => {} },
23
+ argTypes: {
24
+ prefix: { control: { type: 'text' } },
25
+ step: { control: { type: 'text' } },
26
+ suffix: { control: { type: 'text' } },
27
+ type: { control: { type: 'text' } },
28
+ value: { control: false },
29
+ },
30
+ };
31
+ export default meta;
32
+
33
+ export const Default: StoryObj< typeof ValidatedNumberControl > = {
34
+ render: function Template( { onChange, ...args } ) {
35
+ const [ value, setValue ] =
36
+ useState<
37
+ React.ComponentProps< typeof ValidatedNumberControl >[ 'value' ]
38
+ >();
39
+
40
+ return (
41
+ <ValidatedNumberControl
42
+ { ...args }
43
+ value={ value }
44
+ onChange={ ( newValue, ...rest ) => {
45
+ setValue( newValue );
46
+ onChange?.( newValue, ...rest );
47
+ } }
48
+ />
49
+ );
50
+ },
51
+ };
52
+ Default.args = {
53
+ required: true,
54
+ label: 'Number',
55
+ help: 'Odd numbers are not allowed.',
56
+ customValidator: ( value ) => {
57
+ if ( value && parseInt( value.toString(), 10 ) % 2 !== 0 ) {
58
+ return 'Choose an even number.';
59
+ }
60
+ return undefined;
61
+ },
62
+ };
@@ -0,0 +1,52 @@
1
+ import { ArgTypes, Meta, Stories } from '@storybook/blocks';
2
+ import * as OverviewStories from './overview.story';
3
+ import { ValidatedInputControl } from '..';
4
+
5
+ <Meta of={ OverviewStories } title="Components (Private)/Validated Form Controls/Overview" />
6
+
7
+ # Validated Form Controls
8
+
9
+ This section contains form control components that extend [WordPress components](https://wordpress.github.io/gutenberg/) with additional validation capabilities.
10
+
11
+ ## Status: Beta
12
+
13
+ We are still gathering feedback and iterating. Please get in touch with `@WordPress/gutenberg-components` if you are interested in trying them out in the Gutenberg app.
14
+
15
+ ## Usage
16
+
17
+ Component APIs are the same as the underlying WordPress components, with the addition of some optional props:
18
+
19
+ <ArgTypes of={ ValidatedInputControl } include={ [ 'required', 'markWhenOptional', 'customValidator' ] } />
20
+
21
+ ## Implementation
22
+
23
+ These components are designed to work with the native HTML5 [Constraint Validation API](https://developer.mozilla.org/en-US/docs/Web/API/Constraint_validation). Using this browser API as the "source of truth" for form element errors has these benefits:
24
+
25
+ - Ability to use CSS selectors like `:invalid`, `:user-invalid`, `:required`, and `:optional`.
26
+ - Ability to use simple HTML attribute-based constraints (e.g. `min`, `max`, `required`, `pattern`, `type`) together with fully custom validation logic (with the `setCustomValidity()` method).
27
+ - Attribute-based constraints will already have localized, user-facing error messages built in.
28
+ - When a user clicks the form submit button, the browser will automatically check the validity states of all enclosed form elements and block the submit when there are errors. It will also automatically scroll and focus to the first erroring element and show a prominent error popover.
29
+ - While not _all_ aspects of the Constraint Validation API are well-supported in all screen readers yet, many aspects are, and will continue to improve. We can supplement any accessibility shortcomings with things like `aria-live` announcements of error messages.
30
+
31
+ ## Current limitations
32
+
33
+ ### No easy access to underlying validation APIs
34
+
35
+ - The consumer does not have a simple way to trigger their custom validators at an arbitrary point in time. Currently, the validation timings for custom validators are handled by the component — first on `blur`, and then on every `change` after the field is considered "touched". Note that this limitation only applies to **custom** validators, not the standard attribute-based validators like `required` or `pattern`, which can be triggered at any time using the `reportValidity()` method on either the underlying control element or the wrapping `<form>` element.
36
+ - The consumer does not have direct access to the [`validity` object](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) of the underlying element.
37
+
38
+ We don't foresee these being needed much, but it is technically possible by the consumer accessing the target element's validation APIs via `ref`. Better docs or ergonomics can be added when we have actual use cases.
39
+
40
+ ### Delegate elements
41
+
42
+ The implementations for `ToggleGroupControl` and `CustomSelectControl` use a "delegate" element for validation, due to upstream limitations that prevent us from using the actual underlying elements for constraint validation. A delegate element in this context is a visually hidden form element that we "delegate" the Constraint Validation API concerns to.
43
+
44
+ This is not ideal, but lets us maintain a consistent mechanism to scroll to the invalid field when attempting to submit, and block the form from submitting. It is hopefully fine as a stopgap, given the low amount of actual validation that will need happen on these two specific components.
45
+
46
+ ### Controlled mode only
47
+
48
+ Some of the underlying WordPress components currently do not fully support uncontrolled mode. For now, we recommend sticking to controlled mode to avoid any issues.
49
+
50
+ ## Examples
51
+
52
+ <Stories />