@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,69 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { forwardRef, useRef, useEffect } 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 ToggleControl from '../../toggle-control';
13
+ import type { ToggleControlProps } from '../../toggle-control/types';
14
+
15
+ type Value = ToggleControlProps[ 'checked' ];
16
+
17
+ // TODO: Should we customize the default `missingValue` message? It says to "check this box".
18
+
19
+ const UnforwardedValidatedToggleControl = (
20
+ {
21
+ required,
22
+ customValidator,
23
+ onChange,
24
+ markWhenOptional,
25
+ ...restProps
26
+ }: Omit<
27
+ React.ComponentProps< typeof ToggleControl >,
28
+ '__nextHasNoMarginBottom'
29
+ > &
30
+ ValidatedControlProps< Value >,
31
+ forwardedRef: React.ForwardedRef< HTMLInputElement >
32
+ ) => {
33
+ const validityTargetRef = useRef< HTMLInputElement >( null );
34
+ const mergedRefs = useMergeRefs( [ forwardedRef, validityTargetRef ] );
35
+ const valueRef = useRef< Value >( restProps.checked );
36
+
37
+ // TODO: Upstream limitation - The `required` attribute is not passed down to the input,
38
+ // so we need to set it manually.
39
+ useEffect( () => {
40
+ if ( validityTargetRef.current ) {
41
+ validityTargetRef.current.required = required ?? false;
42
+ }
43
+ }, [ required ] );
44
+
45
+ return (
46
+ <ControlWithError
47
+ required={ required }
48
+ markWhenOptional={ markWhenOptional }
49
+ customValidator={ () => {
50
+ return customValidator?.( valueRef.current );
51
+ } }
52
+ getValidityTarget={ () => validityTargetRef.current }
53
+ >
54
+ <ToggleControl
55
+ __nextHasNoMarginBottom
56
+ ref={ mergedRefs }
57
+ onChange={ ( value ) => {
58
+ valueRef.current = value;
59
+ onChange?.( value );
60
+ } }
61
+ { ...restProps }
62
+ />
63
+ </ControlWithError>
64
+ );
65
+ };
66
+
67
+ export const ValidatedToggleControl = forwardRef(
68
+ UnforwardedValidatedToggleControl
69
+ );
@@ -0,0 +1,82 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { forwardRef, useId, 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 { ToggleGroupControl } from '../../toggle-group-control';
12
+ import type { ToggleGroupControlProps } from '../../toggle-group-control/types';
13
+
14
+ type Value = ToggleGroupControlProps[ 'value' ];
15
+
16
+ const UnforwardedValidatedToggleGroupControl = (
17
+ {
18
+ required,
19
+ customValidator,
20
+ onChange,
21
+ markWhenOptional,
22
+ ...restProps
23
+ }: Omit<
24
+ React.ComponentProps< typeof ToggleGroupControl >,
25
+ '__next40pxDefaultSize' | '__nextHasNoMarginBottom'
26
+ > &
27
+ ValidatedControlProps< Value >,
28
+ forwardedRef: React.ForwardedRef< HTMLInputElement >
29
+ ) => {
30
+ const validityTargetRef = useRef< HTMLInputElement >( null );
31
+ const valueRef = useRef< Value >( restProps.value );
32
+
33
+ const nameAttr = useId();
34
+
35
+ return (
36
+ <div className="components-validated-control__wrapper-with-error-delegate">
37
+ <ControlWithError
38
+ required={ required }
39
+ markWhenOptional={ markWhenOptional }
40
+ customValidator={ () => {
41
+ return customValidator?.( valueRef.current );
42
+ } }
43
+ getValidityTarget={ () => validityTargetRef.current }
44
+ >
45
+ <ToggleGroupControl
46
+ __nextHasNoMarginBottom
47
+ __next40pxDefaultSize
48
+ ref={ forwardedRef }
49
+ // TODO: Upstream limitation - In uncontrolled mode, starting from an undefined value then
50
+ // setting a value has a visual bug.
51
+ onChange={ ( value ) => {
52
+ valueRef.current = value;
53
+ onChange?.( value );
54
+ } }
55
+ { ...restProps }
56
+ />
57
+ </ControlWithError>
58
+ <input
59
+ className="components-validated-control__error-delegate"
60
+ type="radio"
61
+ ref={ validityTargetRef }
62
+ required={ required }
63
+ checked={ restProps.value !== null }
64
+ tabIndex={ -1 }
65
+ // A name attribute is needed for the `required` behavior to work.
66
+ name={ nameAttr }
67
+ onChange={ () => {} }
68
+ onFocus={ ( e ) => {
69
+ e.target.previousElementSibling
70
+ ?.querySelector< HTMLButtonElement | HTMLInputElement >(
71
+ '[data-active-item="true"]'
72
+ )
73
+ ?.focus();
74
+ } }
75
+ />
76
+ </div>
77
+ );
78
+ };
79
+
80
+ export const ValidatedToggleGroupControl = forwardRef(
81
+ UnforwardedValidatedToggleGroupControl
82
+ );
@@ -0,0 +1,28 @@
1
+ export type ValidatedControlProps< V > = {
2
+ /**
3
+ * Whether the control is required.
4
+ * @default false
5
+ */
6
+ required?: boolean;
7
+ /**
8
+ * Label the control as "optional" when _not_ `required`, instead of the inverse.
9
+ * @default false
10
+ */
11
+ markWhenOptional?: boolean;
12
+ /**
13
+ * A function that returns a custom validity message when applicable. This error message will be applied to the
14
+ * underlying element using the native [`setCustomValidity()` method](https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity).
15
+ * This means the custom validator will be run _in addition_ to any other HTML attribute-based validation, and
16
+ * will be prioritized over any existing validity messages dictated by the HTML attributes.
17
+ * An empty string or `undefined` return value will clear any existing custom validity message.
18
+ *
19
+ * Make sure you don't programatically pass a value (such as an initial value) to the control component
20
+ * that fails this validator, because the validator will only run for user-initiated changes.
21
+ *
22
+ * Always prefer using standard HTML attributes like `required` and `min`/`max` over custom validators
23
+ * when possible, as they are simpler and have localized error messages built in.
24
+ */
25
+ // TODO: Technically, we could add an optional `customValidity` string prop so the consumer can set
26
+ // an error message at any point in time. We should wait until we have a use case though.
27
+ customValidator?: ( currentValue: V ) => string | void;
28
+ };
@@ -0,0 +1,198 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { __ } from '@wordpress/i18n';
5
+ import { error } from '@wordpress/icons';
6
+
7
+ /**
8
+ * External dependencies
9
+ */
10
+ import {
11
+ cloneElement,
12
+ forwardRef,
13
+ useEffect,
14
+ useState,
15
+ } from '@wordpress/element';
16
+
17
+ /**
18
+ * Internal dependencies
19
+ */
20
+ import { withIgnoreIMEEvents } from '../utils/with-ignore-ime-events';
21
+
22
+ import Icon from '../icon';
23
+
24
+ function appendRequiredIndicator(
25
+ label: React.ReactNode,
26
+ required: boolean | undefined,
27
+ markWhenOptional: boolean | undefined
28
+ ) {
29
+ if ( required && ! markWhenOptional ) {
30
+ return (
31
+ <>
32
+ { label } { `(${ __( 'Required' ) })` }
33
+ </>
34
+ );
35
+ }
36
+ if ( ! required && markWhenOptional ) {
37
+ return (
38
+ <>
39
+ { label } { `(${ __( 'Optional' ) })` }
40
+ </>
41
+ );
42
+ }
43
+ return label;
44
+ }
45
+
46
+ /**
47
+ * HTML elements that support the Constraint Validation API.
48
+ *
49
+ * Here, we exclude HTMLButtonElement because although it does technically support the API,
50
+ * normal buttons are actually exempted from any validation.
51
+ * @see https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation
52
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement/willValidate
53
+ */
54
+ type ValidityTarget =
55
+ | HTMLFieldSetElement
56
+ | HTMLInputElement
57
+ | HTMLSelectElement
58
+ | HTMLTextAreaElement;
59
+
60
+ function UnforwardedControlWithError< C extends React.ReactElement >(
61
+ {
62
+ required,
63
+ markWhenOptional,
64
+ customValidator,
65
+ getValidityTarget,
66
+ children,
67
+ }: {
68
+ /**
69
+ * Whether the control is required.
70
+ */
71
+ required?: boolean;
72
+ /**
73
+ * Label the control as "optional" when _not_ `required`, instead of the inverse.
74
+ */
75
+ markWhenOptional?: boolean;
76
+ /**
77
+ * A function that returns a custom validity message when applicable.
78
+ *
79
+ * This message will be applied to the element returned by `getValidityTarget`.
80
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity
81
+ */
82
+ customValidator?: () => string | void;
83
+ /**
84
+ * A function that returns the actual element on which the validity data should be applied.
85
+ */
86
+ getValidityTarget: () => ValidityTarget | null | undefined;
87
+ /**
88
+ * The control component to apply validation to.
89
+ */
90
+ children: C;
91
+ },
92
+ forwardedRef: React.ForwardedRef< HTMLDivElement >
93
+ ) {
94
+ const [ errorMessage, setErrorMessage ] = useState< string | undefined >();
95
+ const [ isTouched, setIsTouched ] = useState( false );
96
+
97
+ // Ensure that error messages are visible after user attemps to submit a form
98
+ // with multiple invalid fields.
99
+ useEffect( () => {
100
+ const validityTarget = getValidityTarget();
101
+ const showValidationMessage = () =>
102
+ setErrorMessage( validityTarget?.validationMessage );
103
+
104
+ validityTarget?.addEventListener( 'invalid', showValidationMessage );
105
+
106
+ return () => {
107
+ validityTarget?.removeEventListener(
108
+ 'invalid',
109
+ showValidationMessage
110
+ );
111
+ };
112
+ } );
113
+
114
+ const validate = () => {
115
+ const message = customValidator?.();
116
+ const validityTarget = getValidityTarget();
117
+
118
+ validityTarget?.setCustomValidity( message ?? '' );
119
+ setErrorMessage( validityTarget?.validationMessage );
120
+ };
121
+
122
+ const onBlur = ( event: React.FocusEvent< HTMLDivElement > ) => {
123
+ // Only consider "blurred from the component" if focus has fully left the wrapping div.
124
+ // This prevents unnecessary blurs from components with multiple focusable elements.
125
+ if (
126
+ ! event.relatedTarget ||
127
+ ! event.currentTarget.contains( event.relatedTarget )
128
+ ) {
129
+ setIsTouched( true );
130
+
131
+ const validityTarget = getValidityTarget();
132
+
133
+ // Prevents a double flash of the native error tooltip when the control is already showing one.
134
+ if ( ! validityTarget?.validity.valid ) {
135
+ if ( ! errorMessage ) {
136
+ setErrorMessage( validityTarget?.validationMessage );
137
+ }
138
+ return;
139
+ }
140
+
141
+ validate();
142
+ }
143
+ };
144
+
145
+ const onChange = ( ...args: unknown[] ) => {
146
+ children.props.onChange?.( ...args );
147
+
148
+ // Only validate incrementally if the field has blurred at least once,
149
+ // or currently has an error message.
150
+ if ( isTouched || errorMessage ) {
151
+ validate();
152
+ }
153
+ };
154
+
155
+ const onKeyDown = ( event: React.KeyboardEvent< HTMLDivElement > ) => {
156
+ // Ensures that custom validators are triggered when the user submits by pressing Enter,
157
+ // without ever blurring the control.
158
+ if ( event.key === 'Enter' ) {
159
+ validate();
160
+ }
161
+ };
162
+
163
+ return (
164
+ // Disable reason: Just listening to a bubbled event, not for interaction.
165
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
166
+ <div
167
+ className="components-validated-control"
168
+ ref={ forwardedRef }
169
+ onBlur={ onBlur }
170
+ onKeyDown={ withIgnoreIMEEvents( onKeyDown ) }
171
+ >
172
+ { cloneElement( children, {
173
+ label: appendRequiredIndicator(
174
+ children.props.label,
175
+ required,
176
+ markWhenOptional
177
+ ),
178
+ onChange,
179
+ required,
180
+ } ) }
181
+ <div aria-live="polite">
182
+ { errorMessage && (
183
+ <p className="components-validated-control__error">
184
+ <Icon
185
+ className="components-validated-control__error-icon"
186
+ icon={ error }
187
+ size={ 16 }
188
+ fill="currentColor"
189
+ />
190
+ { errorMessage }
191
+ </p>
192
+ ) }
193
+ </div>
194
+ </div>
195
+ );
196
+ }
197
+
198
+ export const ControlWithError = forwardRef( UnforwardedControlWithError );
@@ -0,0 +1,2 @@
1
+ export * from './components';
2
+ export * from './control-with-error';
@@ -0,0 +1,75 @@
1
+ .components-validated-control {
2
+ // For components based on InputBase
3
+ &:has(:is(input, select):user-invalid)
4
+ .components-input-control__backdrop {
5
+ --wp-components-color-accent: $alert-red;
6
+ border-color: $alert-red;
7
+ }
8
+
9
+ // For TextControl, TextareaControl
10
+ :is(textarea, input[type="text"]):user-invalid {
11
+ --wp-admin-theme-color: $alert-red;
12
+ --wp-components-color-accent: $alert-red;
13
+ border-color: $alert-red;
14
+ }
15
+
16
+ // For ComboboxControl
17
+ .components-combobox-control__suggestions-container:has(
18
+ input:user-invalid
19
+ ):not(:has([aria-expanded="true"])) {
20
+ border-color: $alert-red;
21
+ }
22
+ }
23
+
24
+ .components-validated-control__wrapper-with-error-delegate {
25
+ position: relative;
26
+
27
+ // For CustomSelectControl
28
+ &:has(select:user-invalid) .components-input-control__backdrop {
29
+ --wp-components-color-accent: $alert-red;
30
+ border-color: $alert-red;
31
+ }
32
+
33
+ // For ToggleGroupControl
34
+ &:has(input[type="radio"]:invalid) {
35
+ --wp-components-color-accent: $alert-red;
36
+ }
37
+ }
38
+
39
+ .components-validated-control__error-delegate {
40
+ position: absolute;
41
+ top: 0;
42
+ height: 100%;
43
+ width: 100%;
44
+ opacity: 0;
45
+ pointer-events: none;
46
+ }
47
+
48
+ .components-validated-control__error {
49
+ display: flex;
50
+ align-items: flex-start;
51
+ gap: 4px;
52
+ margin: 8px 0 0;
53
+ font-family: $font-family-body;
54
+ font-size: 0.75rem;
55
+ line-height: 16px; // matches the icon size
56
+ color: $alert-red;
57
+ animation:
58
+ components-validated-control__error-jump 0.2s
59
+ cubic-bezier(0.68, -0.55, 0.27, 1.55);
60
+ }
61
+
62
+ .components-validated-control__error-icon {
63
+ flex-shrink: 0;
64
+ }
65
+
66
+ @keyframes components-validated-control__error-jump {
67
+ 0% {
68
+ transform: translateY(-4px);
69
+ opacity: 0;
70
+ }
71
+ 100% {
72
+ transform: translateY(0);
73
+ opacity: 1;
74
+ }
75
+ }