@wordpress/dataviews 10.0.1-next.ff1cebbba.0 → 10.1.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 (399) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +97 -8
  3. package/build/components/dataform/index.js +11 -2
  4. package/build/components/dataform/index.js.map +2 -2
  5. package/build/components/dataform-context/index.js.map +2 -2
  6. package/build/components/dataviews-bulk-actions/index.js +2 -4
  7. package/build/components/dataviews-bulk-actions/index.js.map +2 -2
  8. package/build/components/dataviews-filters/filter.js +9 -4
  9. package/build/components/dataviews-filters/filter.js.map +3 -3
  10. package/build/components/dataviews-filters/search-widget.js +13 -2
  11. package/build/components/dataviews-filters/search-widget.js.map +3 -3
  12. package/build/components/dataviews-filters/use-filters.js +4 -2
  13. package/build/components/dataviews-filters/use-filters.js.map +2 -2
  14. package/build/components/dataviews-item-actions/index.js +6 -6
  15. package/build/components/dataviews-item-actions/index.js.map +2 -2
  16. package/build/dataform-controls/array.js +14 -61
  17. package/build/dataform-controls/array.js.map +3 -3
  18. package/build/dataform-controls/checkbox.js +5 -29
  19. package/build/dataform-controls/checkbox.js.map +3 -3
  20. package/build/dataform-controls/color.js +5 -29
  21. package/build/dataform-controls/color.js.map +3 -3
  22. package/build/dataform-controls/date.js +40 -57
  23. package/build/dataform-controls/date.js.map +3 -3
  24. package/build/dataform-controls/datetime.js +12 -37
  25. package/build/dataform-controls/datetime.js.map +3 -3
  26. package/build/dataform-controls/email.js +3 -1
  27. package/build/dataform-controls/email.js.map +2 -2
  28. package/build/dataform-controls/index.js +2 -1
  29. package/build/dataform-controls/index.js.map +3 -3
  30. package/build/dataform-controls/password.js +3 -1
  31. package/build/dataform-controls/password.js.map +2 -2
  32. package/build/dataform-controls/radio.js +24 -43
  33. package/build/dataform-controls/radio.js.map +3 -3
  34. package/build/dataform-controls/select.js +13 -30
  35. package/build/dataform-controls/select.js.map +3 -3
  36. package/build/dataform-controls/telephone.js +3 -1
  37. package/build/dataform-controls/telephone.js.map +2 -2
  38. package/build/dataform-controls/text.js +3 -1
  39. package/build/dataform-controls/text.js.map +2 -2
  40. package/build/dataform-controls/textarea.js +6 -30
  41. package/build/dataform-controls/textarea.js.map +3 -3
  42. package/build/dataform-controls/toggle-group.js +38 -56
  43. package/build/dataform-controls/toggle-group.js.map +3 -3
  44. package/build/dataform-controls/toggle.js +6 -30
  45. package/build/dataform-controls/toggle.js.map +3 -3
  46. package/build/dataform-controls/url.js +3 -1
  47. package/build/dataform-controls/url.js.map +2 -2
  48. package/build/dataform-controls/utils/get-custom-validity.js +35 -0
  49. package/build/dataform-controls/utils/get-custom-validity.js.map +7 -0
  50. package/build/dataform-controls/utils/validated-input.js +4 -28
  51. package/build/dataform-controls/utils/validated-input.js.map +3 -3
  52. package/build/dataform-controls/utils/validated-number.js +6 -30
  53. package/build/dataform-controls/utils/validated-number.js.map +3 -3
  54. package/build/dataform-layouts/card/index.js +6 -3
  55. package/build/dataform-layouts/card/index.js.map +2 -2
  56. package/build/dataform-layouts/data-form-layout.js +8 -2
  57. package/build/dataform-layouts/data-form-layout.js.map +2 -2
  58. package/build/dataform-layouts/panel/dropdown.js +21 -9
  59. package/build/dataform-layouts/panel/dropdown.js.map +2 -2
  60. package/build/dataform-layouts/panel/index.js +12 -10
  61. package/build/dataform-layouts/panel/index.js.map +2 -2
  62. package/build/dataform-layouts/panel/modal.js +32 -22
  63. package/build/dataform-layouts/panel/modal.js.map +3 -3
  64. package/build/dataform-layouts/regular/index.js +8 -4
  65. package/build/dataform-layouts/regular/index.js.map +2 -2
  66. package/build/dataform-layouts/row/index.js +11 -7
  67. package/build/dataform-layouts/row/index.js.map +2 -2
  68. package/build/dataviews-layouts/list/index.js +7 -7
  69. package/build/dataviews-layouts/list/index.js.map +2 -2
  70. package/build/dataviews-layouts/table/column-header-menu.js +1 -1
  71. package/build/dataviews-layouts/table/column-header-menu.js.map +2 -2
  72. package/build/field-types/boolean.js +3 -2
  73. package/build/field-types/boolean.js.map +3 -3
  74. package/build/field-types/color.js +2 -2
  75. package/build/field-types/color.js.map +3 -3
  76. package/build/field-types/date.js +3 -2
  77. package/build/field-types/date.js.map +3 -3
  78. package/build/field-types/datetime.js +2 -1
  79. package/build/field-types/datetime.js.map +3 -3
  80. package/build/field-types/email.js +2 -1
  81. package/build/field-types/email.js.map +3 -3
  82. package/build/field-types/index.js +2 -1
  83. package/build/field-types/index.js.map +3 -3
  84. package/build/field-types/integer.js +2 -1
  85. package/build/field-types/integer.js.map +3 -3
  86. package/build/field-types/number.js +18 -9
  87. package/build/field-types/number.js.map +3 -3
  88. package/build/field-types/password.js +2 -1
  89. package/build/field-types/password.js.map +3 -3
  90. package/build/field-types/telephone.js +2 -1
  91. package/build/field-types/telephone.js.map +3 -3
  92. package/build/field-types/text.js +2 -1
  93. package/build/field-types/text.js.map +3 -3
  94. package/build/field-types/url.js +2 -1
  95. package/build/field-types/url.js.map +3 -3
  96. package/build/field-types/utils/render-from-elements.js +24 -3
  97. package/build/field-types/utils/render-from-elements.js.map +4 -4
  98. package/build/hooks/index.js +39 -0
  99. package/build/hooks/index.js.map +7 -0
  100. package/build/hooks/use-elements.js +63 -0
  101. package/build/hooks/use-elements.js.map +7 -0
  102. package/build/hooks/use-form-validity.js +426 -0
  103. package/build/hooks/use-form-validity.js.map +7 -0
  104. package/build/index.js +3 -3
  105. package/build/index.js.map +2 -2
  106. package/build/types/dataform.js.map +1 -1
  107. package/build/types/dataviews.js.map +1 -1
  108. package/build/types/field-api.js.map +1 -1
  109. package/build/utils/has-elements.js +27 -0
  110. package/build/utils/has-elements.js.map +7 -0
  111. package/build/utils/normalize-fields.js +4 -2
  112. package/build/utils/normalize-fields.js.map +3 -3
  113. package/build-module/components/dataform/index.js +11 -2
  114. package/build-module/components/dataform/index.js.map +2 -2
  115. package/build-module/components/dataform-context/index.js.map +2 -2
  116. package/build-module/components/dataviews-bulk-actions/index.js +2 -4
  117. package/build-module/components/dataviews-bulk-actions/index.js.map +2 -2
  118. package/build-module/components/dataviews-filters/filter.js +9 -4
  119. package/build-module/components/dataviews-filters/filter.js.map +2 -2
  120. package/build-module/components/dataviews-filters/search-widget.js +19 -3
  121. package/build-module/components/dataviews-filters/search-widget.js.map +2 -2
  122. package/build-module/components/dataviews-filters/use-filters.js +4 -2
  123. package/build-module/components/dataviews-filters/use-filters.js.map +2 -2
  124. package/build-module/components/dataviews-item-actions/index.js +6 -6
  125. package/build-module/components/dataviews-item-actions/index.js.map +2 -2
  126. package/build-module/dataform-controls/array.js +16 -63
  127. package/build-module/dataform-controls/array.js.map +2 -2
  128. package/build-module/dataform-controls/checkbox.js +6 -30
  129. package/build-module/dataform-controls/checkbox.js.map +2 -2
  130. package/build-module/dataform-controls/color.js +6 -30
  131. package/build-module/dataform-controls/color.js.map +2 -2
  132. package/build-module/dataform-controls/date.js +49 -66
  133. package/build-module/dataform-controls/date.js.map +2 -2
  134. package/build-module/dataform-controls/datetime.js +14 -39
  135. package/build-module/dataform-controls/datetime.js.map +2 -2
  136. package/build-module/dataform-controls/email.js +3 -1
  137. package/build-module/dataform-controls/email.js.map +2 -2
  138. package/build-module/dataform-controls/index.js +2 -1
  139. package/build-module/dataform-controls/index.js.map +2 -2
  140. package/build-module/dataform-controls/password.js +3 -1
  141. package/build-module/dataform-controls/password.js.map +2 -2
  142. package/build-module/dataform-controls/radio.js +26 -45
  143. package/build-module/dataform-controls/radio.js.map +2 -2
  144. package/build-module/dataform-controls/select.js +15 -32
  145. package/build-module/dataform-controls/select.js.map +2 -2
  146. package/build-module/dataform-controls/telephone.js +3 -1
  147. package/build-module/dataform-controls/telephone.js.map +2 -2
  148. package/build-module/dataform-controls/text.js +3 -1
  149. package/build-module/dataform-controls/text.js.map +2 -2
  150. package/build-module/dataform-controls/textarea.js +7 -31
  151. package/build-module/dataform-controls/textarea.js.map +2 -2
  152. package/build-module/dataform-controls/toggle-group.js +41 -58
  153. package/build-module/dataform-controls/toggle-group.js.map +2 -2
  154. package/build-module/dataform-controls/toggle.js +7 -31
  155. package/build-module/dataform-controls/toggle.js.map +2 -2
  156. package/build-module/dataform-controls/url.js +3 -1
  157. package/build-module/dataform-controls/url.js.map +2 -2
  158. package/build-module/dataform-controls/utils/get-custom-validity.js +15 -0
  159. package/build-module/dataform-controls/utils/get-custom-validity.js.map +7 -0
  160. package/build-module/dataform-controls/utils/validated-input.js +5 -29
  161. package/build-module/dataform-controls/utils/validated-input.js.map +2 -2
  162. package/build-module/dataform-controls/utils/validated-number.js +7 -31
  163. package/build-module/dataform-controls/utils/validated-number.js.map +2 -2
  164. package/build-module/dataform-layouts/card/index.js +6 -3
  165. package/build-module/dataform-layouts/card/index.js.map +2 -2
  166. package/build-module/dataform-layouts/data-form-layout.js +8 -2
  167. package/build-module/dataform-layouts/data-form-layout.js.map +2 -2
  168. package/build-module/dataform-layouts/panel/dropdown.js +21 -9
  169. package/build-module/dataform-layouts/panel/dropdown.js.map +2 -2
  170. package/build-module/dataform-layouts/panel/index.js +12 -10
  171. package/build-module/dataform-layouts/panel/index.js.map +2 -2
  172. package/build-module/dataform-layouts/panel/modal.js +33 -23
  173. package/build-module/dataform-layouts/panel/modal.js.map +2 -2
  174. package/build-module/dataform-layouts/regular/index.js +8 -4
  175. package/build-module/dataform-layouts/regular/index.js.map +2 -2
  176. package/build-module/dataform-layouts/row/index.js +11 -7
  177. package/build-module/dataform-layouts/row/index.js.map +2 -2
  178. package/build-module/dataviews-layouts/list/index.js +7 -7
  179. package/build-module/dataviews-layouts/list/index.js.map +2 -2
  180. package/build-module/dataviews-layouts/table/column-header-menu.js +1 -1
  181. package/build-module/dataviews-layouts/table/column-header-menu.js.map +2 -2
  182. package/build-module/field-types/boolean.js +4 -3
  183. package/build-module/field-types/boolean.js.map +2 -2
  184. package/build-module/field-types/color.js +3 -3
  185. package/build-module/field-types/color.js.map +2 -2
  186. package/build-module/field-types/date.js +4 -3
  187. package/build-module/field-types/date.js.map +2 -2
  188. package/build-module/field-types/datetime.js +3 -2
  189. package/build-module/field-types/datetime.js.map +2 -2
  190. package/build-module/field-types/email.js +3 -2
  191. package/build-module/field-types/email.js.map +2 -2
  192. package/build-module/field-types/index.js +3 -2
  193. package/build-module/field-types/index.js.map +2 -2
  194. package/build-module/field-types/integer.js +3 -2
  195. package/build-module/field-types/integer.js.map +2 -2
  196. package/build-module/field-types/number.js +8 -9
  197. package/build-module/field-types/number.js.map +2 -2
  198. package/build-module/field-types/password.js +3 -2
  199. package/build-module/field-types/password.js.map +2 -2
  200. package/build-module/field-types/telephone.js +3 -2
  201. package/build-module/field-types/telephone.js.map +2 -2
  202. package/build-module/field-types/text.js +3 -2
  203. package/build-module/field-types/text.js.map +2 -2
  204. package/build-module/field-types/url.js +3 -2
  205. package/build-module/field-types/url.js.map +2 -2
  206. package/build-module/field-types/utils/render-from-elements.js +14 -3
  207. package/build-module/field-types/utils/render-from-elements.js.map +3 -3
  208. package/build-module/hooks/index.js +5 -0
  209. package/build-module/hooks/index.js.map +7 -0
  210. package/build-module/hooks/use-elements.js +43 -0
  211. package/build-module/hooks/use-elements.js.map +7 -0
  212. package/build-module/hooks/use-form-validity.js +392 -0
  213. package/build-module/hooks/use-form-validity.js.map +7 -0
  214. package/build-module/index.js +2 -2
  215. package/build-module/index.js.map +2 -2
  216. package/build-module/utils/has-elements.js +7 -0
  217. package/build-module/utils/has-elements.js.map +7 -0
  218. package/build-module/utils/normalize-fields.js +4 -2
  219. package/build-module/utils/normalize-fields.js.map +2 -2
  220. package/build-style/style-rtl.css +10 -4
  221. package/build-style/style.css +10 -4
  222. package/build-types/components/dataform/index.d.ts +1 -1
  223. package/build-types/components/dataform/index.d.ts.map +1 -1
  224. package/build-types/components/dataform-context/index.d.ts.map +1 -1
  225. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
  226. package/build-types/components/dataviews-filters/filter.d.ts +1 -1
  227. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  228. package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
  229. package/build-types/components/dataviews-filters/use-filters.d.ts.map +1 -1
  230. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
  231. package/build-types/dataform-controls/array.d.ts +1 -1
  232. package/build-types/dataform-controls/array.d.ts.map +1 -1
  233. package/build-types/dataform-controls/checkbox.d.ts +1 -1
  234. package/build-types/dataform-controls/checkbox.d.ts.map +1 -1
  235. package/build-types/dataform-controls/color.d.ts +1 -1
  236. package/build-types/dataform-controls/color.d.ts.map +1 -1
  237. package/build-types/dataform-controls/date.d.ts +1 -1
  238. package/build-types/dataform-controls/date.d.ts.map +1 -1
  239. package/build-types/dataform-controls/datetime.d.ts +1 -1
  240. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  241. package/build-types/dataform-controls/email.d.ts +1 -1
  242. package/build-types/dataform-controls/email.d.ts.map +1 -1
  243. package/build-types/dataform-controls/index.d.ts.map +1 -1
  244. package/build-types/dataform-controls/password.d.ts +1 -1
  245. package/build-types/dataform-controls/password.d.ts.map +1 -1
  246. package/build-types/dataform-controls/radio.d.ts +1 -1
  247. package/build-types/dataform-controls/radio.d.ts.map +1 -1
  248. package/build-types/dataform-controls/select.d.ts +1 -1
  249. package/build-types/dataform-controls/select.d.ts.map +1 -1
  250. package/build-types/dataform-controls/telephone.d.ts +1 -1
  251. package/build-types/dataform-controls/telephone.d.ts.map +1 -1
  252. package/build-types/dataform-controls/text.d.ts +1 -1
  253. package/build-types/dataform-controls/text.d.ts.map +1 -1
  254. package/build-types/dataform-controls/textarea.d.ts +1 -1
  255. package/build-types/dataform-controls/textarea.d.ts.map +1 -1
  256. package/build-types/dataform-controls/toggle-group.d.ts +1 -1
  257. package/build-types/dataform-controls/toggle-group.d.ts.map +1 -1
  258. package/build-types/dataform-controls/toggle.d.ts +1 -1
  259. package/build-types/dataform-controls/toggle.d.ts.map +1 -1
  260. package/build-types/dataform-controls/url.d.ts +1 -1
  261. package/build-types/dataform-controls/url.d.ts.map +1 -1
  262. package/build-types/dataform-controls/utils/get-custom-validity.d.ts +9 -0
  263. package/build-types/dataform-controls/utils/get-custom-validity.d.ts.map +1 -0
  264. package/build-types/dataform-controls/utils/validated-input.d.ts +1 -1
  265. package/build-types/dataform-controls/utils/validated-input.d.ts.map +1 -1
  266. package/build-types/dataform-controls/utils/validated-number.d.ts +1 -1
  267. package/build-types/dataform-controls/utils/validated-number.d.ts.map +1 -1
  268. package/build-types/dataform-layouts/card/index.d.ts +1 -1
  269. package/build-types/dataform-layouts/card/index.d.ts.map +1 -1
  270. package/build-types/dataform-layouts/data-form-layout.d.ts +5 -3
  271. package/build-types/dataform-layouts/data-form-layout.d.ts.map +1 -1
  272. package/build-types/dataform-layouts/panel/dropdown.d.ts +8 -7
  273. package/build-types/dataform-layouts/panel/dropdown.d.ts.map +1 -1
  274. package/build-types/dataform-layouts/panel/index.d.ts +1 -1
  275. package/build-types/dataform-layouts/panel/index.d.ts.map +1 -1
  276. package/build-types/dataform-layouts/panel/modal.d.ts +5 -5
  277. package/build-types/dataform-layouts/panel/modal.d.ts.map +1 -1
  278. package/build-types/dataform-layouts/regular/index.d.ts +1 -1
  279. package/build-types/dataform-layouts/regular/index.d.ts.map +1 -1
  280. package/build-types/dataform-layouts/row/index.d.ts +1 -1
  281. package/build-types/dataform-layouts/row/index.d.ts.map +1 -1
  282. package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
  283. package/build-types/field-types/boolean.d.ts +1 -1
  284. package/build-types/field-types/date.d.ts +1 -1
  285. package/build-types/field-types/datetime.d.ts.map +1 -1
  286. package/build-types/field-types/email.d.ts.map +1 -1
  287. package/build-types/field-types/index.d.ts.map +1 -1
  288. package/build-types/field-types/integer.d.ts.map +1 -1
  289. package/build-types/field-types/number.d.ts +1 -1
  290. package/build-types/field-types/number.d.ts.map +1 -1
  291. package/build-types/field-types/password.d.ts +1 -1
  292. package/build-types/field-types/password.d.ts.map +1 -1
  293. package/build-types/field-types/telephone.d.ts.map +1 -1
  294. package/build-types/field-types/text.d.ts.map +1 -1
  295. package/build-types/field-types/url.d.ts.map +1 -1
  296. package/build-types/field-types/utils/render-from-elements.d.ts +1 -1
  297. package/build-types/field-types/utils/render-from-elements.d.ts.map +1 -1
  298. package/build-types/hooks/index.d.ts +5 -0
  299. package/build-types/hooks/index.d.ts.map +1 -0
  300. package/build-types/hooks/use-elements.d.ts +12 -0
  301. package/build-types/hooks/use-elements.d.ts.map +1 -0
  302. package/build-types/hooks/use-form-validity.d.ts +16 -0
  303. package/build-types/hooks/use-form-validity.d.ts.map +1 -0
  304. package/build-types/index.d.ts +1 -1
  305. package/build-types/index.d.ts.map +1 -1
  306. package/build-types/stories/dataform.story.d.ts +14 -4
  307. package/build-types/stories/dataform.story.d.ts.map +1 -1
  308. package/build-types/stories/dataviews.fixtures.d.ts.map +1 -1
  309. package/build-types/stories/field-types.story.d.ts +38 -15
  310. package/build-types/stories/field-types.story.d.ts.map +1 -1
  311. package/build-types/test/use-form-validity.d.ts +2 -0
  312. package/build-types/test/use-form-validity.d.ts.map +1 -0
  313. package/build-types/types/dataform.d.ts +4 -1
  314. package/build-types/types/dataform.d.ts.map +1 -1
  315. package/build-types/types/dataviews.d.ts +10 -2
  316. package/build-types/types/dataviews.d.ts.map +1 -1
  317. package/build-types/types/field-api.d.ts +25 -1
  318. package/build-types/types/field-api.d.ts.map +1 -1
  319. package/build-types/utils/has-elements.d.ts +6 -0
  320. package/build-types/utils/has-elements.d.ts.map +1 -0
  321. package/build-types/utils/normalize-fields.d.ts.map +1 -1
  322. package/build-wp/index.js +1011 -819
  323. package/package.json +15 -15
  324. package/src/components/dataform/index.tsx +7 -1
  325. package/src/components/dataform-context/index.tsx +3 -1
  326. package/src/components/dataviews-bulk-actions/index.tsx +3 -5
  327. package/src/components/dataviews-filters/filter.tsx +11 -5
  328. package/src/components/dataviews-filters/search-widget.tsx +30 -3
  329. package/src/components/dataviews-filters/style.scss +8 -0
  330. package/src/components/dataviews-filters/use-filters.ts +4 -2
  331. package/src/components/dataviews-item-actions/index.tsx +11 -6
  332. package/src/components/dataviews-item-actions/style.scss +1 -0
  333. package/src/dataform-controls/array.tsx +16 -82
  334. package/src/dataform-controls/checkbox.tsx +5 -41
  335. package/src/dataform-controls/color.tsx +5 -37
  336. package/src/dataform-controls/date.tsx +63 -76
  337. package/src/dataform-controls/datetime.tsx +11 -45
  338. package/src/dataform-controls/email.tsx +2 -0
  339. package/src/dataform-controls/index.tsx +2 -1
  340. package/src/dataform-controls/password.tsx +2 -0
  341. package/src/dataform-controls/radio.tsx +24 -55
  342. package/src/dataform-controls/select.tsx +14 -42
  343. package/src/dataform-controls/telephone.tsx +2 -0
  344. package/src/dataform-controls/text.tsx +2 -0
  345. package/src/dataform-controls/textarea.tsx +6 -42
  346. package/src/dataform-controls/toggle-group.tsx +38 -64
  347. package/src/dataform-controls/toggle.tsx +6 -42
  348. package/src/dataform-controls/url.tsx +2 -0
  349. package/src/dataform-controls/utils/get-custom-validity.ts +24 -0
  350. package/src/dataform-controls/utils/validated-input.tsx +4 -40
  351. package/src/dataform-controls/utils/validated-number.tsx +6 -44
  352. package/src/dataform-layouts/card/index.tsx +3 -0
  353. package/src/dataform-layouts/data-form-layout.tsx +18 -3
  354. package/src/dataform-layouts/panel/dropdown.tsx +35 -14
  355. package/src/dataform-layouts/panel/index.tsx +9 -7
  356. package/src/dataform-layouts/panel/modal.tsx +41 -30
  357. package/src/dataform-layouts/regular/index.tsx +4 -0
  358. package/src/dataform-layouts/row/index.tsx +8 -4
  359. package/src/dataviews-layouts/list/index.tsx +9 -7
  360. package/src/dataviews-layouts/table/column-header-menu.tsx +1 -1
  361. package/src/dataviews-layouts/table/style.scss +2 -3
  362. package/src/field-types/boolean.tsx +3 -3
  363. package/src/field-types/color.tsx +3 -3
  364. package/src/field-types/date.tsx +3 -3
  365. package/src/field-types/datetime.tsx +6 -4
  366. package/src/field-types/email.tsx +6 -4
  367. package/src/field-types/index.tsx +6 -4
  368. package/src/field-types/integer.tsx +6 -4
  369. package/src/field-types/number.tsx +8 -12
  370. package/src/field-types/password.tsx +6 -4
  371. package/src/field-types/telephone.tsx +6 -4
  372. package/src/field-types/text.tsx +6 -4
  373. package/src/field-types/url.tsx +6 -4
  374. package/src/field-types/utils/render-from-elements.tsx +29 -0
  375. package/src/hooks/index.ts +4 -0
  376. package/src/hooks/use-elements.ts +66 -0
  377. package/src/hooks/use-form-validity.ts +571 -0
  378. package/src/index.ts +1 -1
  379. package/src/stories/dataform.story.tsx +243 -59
  380. package/src/stories/dataviews.fixtures.tsx +9 -4
  381. package/src/stories/field-types.story.tsx +159 -29
  382. package/src/test/use-form-validity.ts +546 -0
  383. package/src/types/dataform.ts +5 -1
  384. package/src/types/dataviews.ts +12 -2
  385. package/src/types/field-api.ts +32 -1
  386. package/src/utils/has-elements.ts +11 -0
  387. package/src/utils/normalize-fields.ts +7 -2
  388. package/tsconfig.tsbuildinfo +1 -1
  389. package/build/utils/is-item-valid.js +0 -75
  390. package/build/utils/is-item-valid.js.map +0 -7
  391. package/build-module/utils/is-item-valid.js +0 -45
  392. package/build-module/utils/is-item-valid.js.map +0 -7
  393. package/build-types/test/validation.d.ts +0 -2
  394. package/build-types/test/validation.d.ts.map +0 -1
  395. package/build-types/utils/is-item-valid.d.ts +0 -12
  396. package/build-types/utils/is-item-valid.d.ts.map +0 -1
  397. package/src/field-types/utils/render-from-elements.ts +0 -15
  398. package/src/test/validation.ts +0 -322
  399. package/src/utils/is-item-valid.ts +0 -93
@@ -0,0 +1,66 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import { useEffect, useState } from '@wordpress/element';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import type { Option } from '../types';
10
+
11
+ const EMPTY_ARRAY: Option[] = [];
12
+
13
+ export default function useElements( {
14
+ elements,
15
+ getElements,
16
+ }: {
17
+ elements?: Option[];
18
+ getElements?: () => Promise< Option[] >;
19
+ } ) {
20
+ const staticElements =
21
+ Array.isArray( elements ) && elements.length > 0
22
+ ? elements
23
+ : EMPTY_ARRAY;
24
+ const [ records, setRecords ] = useState< Option[] >( staticElements );
25
+ const [ isLoading, setIsLoading ] = useState( false );
26
+
27
+ useEffect( () => {
28
+ if ( ! getElements ) {
29
+ setRecords( staticElements );
30
+ return;
31
+ }
32
+
33
+ let cancelled = false;
34
+ setIsLoading( true );
35
+ getElements()
36
+ .then( ( fetchedElements ) => {
37
+ if ( ! cancelled ) {
38
+ const dynamicElements =
39
+ Array.isArray( fetchedElements ) &&
40
+ fetchedElements.length > 0
41
+ ? fetchedElements
42
+ : staticElements;
43
+ setRecords( dynamicElements );
44
+ }
45
+ } )
46
+ .catch( () => {
47
+ if ( ! cancelled ) {
48
+ setRecords( staticElements );
49
+ }
50
+ } )
51
+ .finally( () => {
52
+ if ( ! cancelled ) {
53
+ setIsLoading( false );
54
+ }
55
+ } );
56
+
57
+ return () => {
58
+ cancelled = true;
59
+ };
60
+ }, [ getElements, staticElements ] );
61
+
62
+ return {
63
+ elements: records,
64
+ isLoading,
65
+ };
66
+ }
@@ -0,0 +1,571 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import deepMerge from 'deepmerge';
5
+
6
+ /**
7
+ * WordPress dependencies
8
+ */
9
+ import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
10
+ import { __ } from '@wordpress/i18n';
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ import normalizeFields from '../utils/normalize-fields';
16
+ import normalizeFormFields from '../dataform-layouts/normalize-form-fields';
17
+ import type {
18
+ CombinedFormField,
19
+ Field,
20
+ FieldValidity,
21
+ Form,
22
+ FormValidity,
23
+ NormalizedField,
24
+ } from '../types';
25
+ const isEmptyNullOrUndefined = ( value: any ) =>
26
+ [ undefined, '', null ].includes( value );
27
+
28
+ const isArrayOrElementsEmptyNullOrUndefined = ( value: any ) => {
29
+ return (
30
+ ! Array.isArray( value ) ||
31
+ value.length === 0 ||
32
+ value.every( ( element: any ) => isEmptyNullOrUndefined( element ) )
33
+ );
34
+ };
35
+
36
+ function isInvalidForRequired( fieldType: string | undefined, value: any ) {
37
+ if (
38
+ ( fieldType === undefined && isEmptyNullOrUndefined( value ) ) ||
39
+ ( fieldType === 'text' && isEmptyNullOrUndefined( value ) ) ||
40
+ ( fieldType === 'email' && isEmptyNullOrUndefined( value ) ) ||
41
+ ( fieldType === 'url' && isEmptyNullOrUndefined( value ) ) ||
42
+ ( fieldType === 'telephone' && isEmptyNullOrUndefined( value ) ) ||
43
+ ( fieldType === 'password' && isEmptyNullOrUndefined( value ) ) ||
44
+ ( fieldType === 'integer' && isEmptyNullOrUndefined( value ) ) ||
45
+ ( fieldType === 'number' && isEmptyNullOrUndefined( value ) ) ||
46
+ ( fieldType === 'array' &&
47
+ isArrayOrElementsEmptyNullOrUndefined( value ) ) ||
48
+ ( fieldType === 'boolean' && value !== true )
49
+ ) {
50
+ return true;
51
+ }
52
+
53
+ return false;
54
+ }
55
+
56
+ function isFormValid( formValidity: FormValidity | undefined ): boolean {
57
+ if ( ! formValidity ) {
58
+ return true;
59
+ }
60
+
61
+ return Object.values( formValidity ).every( ( fieldValidation ) => {
62
+ return Object.entries( fieldValidation ).every(
63
+ ( [ key, validation ] ) => {
64
+ if (
65
+ key === 'children' &&
66
+ validation &&
67
+ typeof validation === 'object'
68
+ ) {
69
+ // Recursively check children validations
70
+ return isFormValid( validation as FormValidity );
71
+ }
72
+ return validation.type === 'valid';
73
+ }
74
+ );
75
+ } );
76
+ }
77
+
78
+ function updateFieldValidity(
79
+ setFormValidity: React.Dispatch< React.SetStateAction< FormValidity > >,
80
+ parentFieldId: string | undefined,
81
+ fieldId: string,
82
+ newValidity: FieldValidity
83
+ ) {
84
+ if ( parentFieldId ) {
85
+ setFormValidity( ( prev ) => ( {
86
+ ...prev,
87
+ [ parentFieldId ]: {
88
+ ...prev?.[ parentFieldId ],
89
+ children: {
90
+ ...prev?.[ parentFieldId ]?.children,
91
+ [ fieldId ]: {
92
+ ...newValidity,
93
+ },
94
+ },
95
+ },
96
+ } ) );
97
+ } else {
98
+ setFormValidity( ( prev ) => ( {
99
+ ...prev,
100
+ [ fieldId ]: {
101
+ ...newValidity,
102
+ },
103
+ } ) );
104
+ }
105
+ }
106
+
107
+ function getFieldsToValidate< Item >(
108
+ fields: Field< Item >[],
109
+ form: Form
110
+ ): {
111
+ fields: NormalizedField< Item >[];
112
+ fieldToParent: Map< string, string >;
113
+ } {
114
+ const formFields = normalizeFormFields( form );
115
+ if ( formFields.length === 0 ) {
116
+ return { fields: [], fieldToParent: new Map() };
117
+ }
118
+
119
+ const fieldToParent = new Map< string, string >();
120
+ const fieldIdsToValidate: string[] = [];
121
+ formFields.forEach( ( formField ) => {
122
+ if ( !! ( formField as CombinedFormField ).children ) {
123
+ ( formField as CombinedFormField ).children.forEach( ( child ) => {
124
+ const childId = typeof child === 'string' ? child : child.id;
125
+ fieldIdsToValidate.push( childId );
126
+ fieldToParent.set( childId, formField.id );
127
+ } );
128
+ } else {
129
+ fieldIdsToValidate.push( formField.id );
130
+ }
131
+ } );
132
+
133
+ return {
134
+ fields: normalizeFields(
135
+ fields.filter( ( field ) =>
136
+ fieldIdsToValidate.includes( field.id )
137
+ )
138
+ ),
139
+ fieldToParent,
140
+ };
141
+ }
142
+
143
+ /**
144
+ * Hook that validates a form item and returns an object with error messages for each field.
145
+ *
146
+ * @param item The item to validate.
147
+ * @param fields Fields config.
148
+ * @param form Form config.
149
+ *
150
+ * @return Record of field IDs to error messages (undefined means no error).
151
+ */
152
+ export function useFormValidity< Item >(
153
+ item: Item,
154
+ fields: Field< Item >[],
155
+ form: Form
156
+ ): { validity: FormValidity; isValid: boolean } {
157
+ const [ formValidity, setFormValidity ] = useState< FormValidity >();
158
+ const previousValidatedValuesRef = useRef< Record< string, any > >( {} );
159
+
160
+ // The following counters are used to track the validation promises triggered
161
+ // by executing isValid.custom and the elements validation. When the promise resolves,
162
+ // it will update the form validity state ONLY if its counter matches the current one.
163
+ const customValidationCounterRef = useRef< Record< string, number > >( {} );
164
+ const elementsValidationCounterRef = useRef< Record< string, number > >(
165
+ {}
166
+ );
167
+
168
+ const validate = useCallback( () => {
169
+ const { fields: fieldsToValidate, fieldToParent } = getFieldsToValidate(
170
+ fields,
171
+ form
172
+ );
173
+ if ( fieldsToValidate.length === 0 ) {
174
+ setFormValidity( undefined );
175
+ return;
176
+ }
177
+
178
+ fieldsToValidate.forEach( ( field ) => {
179
+ const value = field.getValue( { item } );
180
+ if (
181
+ previousValidatedValuesRef.current.hasOwnProperty( field.id ) &&
182
+ value === previousValidatedValuesRef.current[ field.id ]
183
+ ) {
184
+ return;
185
+ }
186
+ previousValidatedValuesRef.current[ field.id ] = value;
187
+
188
+ const parentFieldId = fieldToParent.get( field.id );
189
+
190
+ // isValid.required
191
+ if (
192
+ field.isValid.required &&
193
+ isInvalidForRequired( field.type, value )
194
+ ) {
195
+ updateFieldValidity( setFormValidity, parentFieldId, field.id, {
196
+ required: { type: 'invalid' },
197
+ } );
198
+ return;
199
+ }
200
+
201
+ // isValid.elements (static elements)
202
+ if (
203
+ field.isValid.elements &&
204
+ field.hasElements &&
205
+ ! field.getElements &&
206
+ Array.isArray( field.elements )
207
+ ) {
208
+ const validValues = field.elements.map( ( el ) => el.value );
209
+
210
+ if (
211
+ field.type !== 'array' &&
212
+ ! validValues.includes( value )
213
+ ) {
214
+ updateFieldValidity(
215
+ setFormValidity,
216
+ parentFieldId,
217
+ field.id,
218
+ {
219
+ elements: {
220
+ type: 'invalid',
221
+ message: 'Value must be one of the elements.',
222
+ },
223
+ }
224
+ );
225
+ return;
226
+ }
227
+
228
+ if ( field.type === 'array' && ! Array.isArray( value ) ) {
229
+ updateFieldValidity(
230
+ setFormValidity,
231
+ parentFieldId,
232
+ field.id,
233
+ {
234
+ elements: {
235
+ type: 'invalid',
236
+ message: 'Value must be an array.',
237
+ },
238
+ }
239
+ );
240
+ return;
241
+ }
242
+ if (
243
+ field.type === 'array' &&
244
+ value.some( ( v: any ) => ! validValues.includes( v ) )
245
+ ) {
246
+ updateFieldValidity(
247
+ setFormValidity,
248
+ parentFieldId,
249
+ field.id,
250
+ {
251
+ elements: {
252
+ type: 'invalid',
253
+ message: 'Value must be one of the elements.',
254
+ },
255
+ }
256
+ );
257
+ return;
258
+ }
259
+ }
260
+
261
+ // isValid.elements (get them via getElements first)
262
+ if (
263
+ field.isValid.elements &&
264
+ field.hasElements &&
265
+ typeof field.getElements === 'function'
266
+ ) {
267
+ const currentToken =
268
+ ( elementsValidationCounterRef.current[ field.id ] || 0 ) +
269
+ 1;
270
+ elementsValidationCounterRef.current[ field.id ] = currentToken;
271
+ updateFieldValidity( setFormValidity, parentFieldId, field.id, {
272
+ elements: {
273
+ type: 'validating',
274
+ message: 'Validating...',
275
+ },
276
+ } );
277
+
278
+ field
279
+ .getElements()
280
+ .then( ( result ) => {
281
+ if (
282
+ elementsValidationCounterRef.current[ field.id ] !==
283
+ currentToken
284
+ ) {
285
+ return;
286
+ }
287
+
288
+ if ( ! Array.isArray( result ) ) {
289
+ updateFieldValidity(
290
+ setFormValidity,
291
+ parentFieldId,
292
+ field.id,
293
+ {
294
+ elements: {
295
+ type: 'invalid',
296
+ message: 'Could not validate elements.',
297
+ },
298
+ }
299
+ );
300
+ return;
301
+ }
302
+
303
+ const validValues = result.map( ( el ) => el.value );
304
+ if (
305
+ field.type !== 'array' &&
306
+ ! validValues.includes( value )
307
+ ) {
308
+ updateFieldValidity(
309
+ setFormValidity,
310
+ parentFieldId,
311
+ field.id,
312
+ {
313
+ elements: {
314
+ type: 'invalid',
315
+ message:
316
+ 'Value must be one of the elements.',
317
+ },
318
+ }
319
+ );
320
+ return;
321
+ }
322
+
323
+ if (
324
+ field.type === 'array' &&
325
+ ! Array.isArray( value )
326
+ ) {
327
+ updateFieldValidity(
328
+ setFormValidity,
329
+ parentFieldId,
330
+ field.id,
331
+ {
332
+ elements: {
333
+ type: 'invalid',
334
+ message: 'Value must be an array.',
335
+ },
336
+ }
337
+ );
338
+ return;
339
+ }
340
+
341
+ if (
342
+ field.type === 'array' &&
343
+ value.some(
344
+ ( v: any ) => ! validValues.includes( v )
345
+ )
346
+ ) {
347
+ updateFieldValidity(
348
+ setFormValidity,
349
+ parentFieldId,
350
+ field.id,
351
+ {
352
+ elements: {
353
+ type: 'invalid',
354
+ message:
355
+ 'Value must be one of the elements.',
356
+ },
357
+ }
358
+ );
359
+ }
360
+ } )
361
+ .catch( ( error ) => {
362
+ if (
363
+ elementsValidationCounterRef.current[ field.id ] !==
364
+ currentToken
365
+ ) {
366
+ return;
367
+ }
368
+
369
+ updateFieldValidity(
370
+ setFormValidity,
371
+ parentFieldId,
372
+ field.id,
373
+ {
374
+ elements: {
375
+ type: 'invalid',
376
+ message: error.message,
377
+ },
378
+ }
379
+ );
380
+ } );
381
+ }
382
+
383
+ // Check isValid.custom
384
+ let customError;
385
+ try {
386
+ customError = field.isValid?.custom?.(
387
+ deepMerge(
388
+ item,
389
+ field.setValue( {
390
+ item,
391
+ value,
392
+ } ) as Partial< Item >
393
+ ),
394
+ field
395
+ );
396
+ } catch ( error: any ) {
397
+ let errorMessage;
398
+ if ( error instanceof Error ) {
399
+ errorMessage = error.message;
400
+ } else {
401
+ errorMessage =
402
+ String( error ) ||
403
+ __( 'Unknown error when running custom validation.' );
404
+ }
405
+
406
+ updateFieldValidity( setFormValidity, parentFieldId, field.id, {
407
+ custom: {
408
+ type: 'invalid',
409
+ message: errorMessage,
410
+ },
411
+ } );
412
+ }
413
+
414
+ // — isValid.custom (sync version)
415
+ if ( typeof customError === 'string' ) {
416
+ updateFieldValidity( setFormValidity, parentFieldId, field.id, {
417
+ custom: {
418
+ type: 'invalid',
419
+ message: customError,
420
+ },
421
+ } );
422
+ return;
423
+ }
424
+
425
+ // — isValid.custom (async version)
426
+ if ( customError instanceof Promise ) {
427
+ // Increment token for this field to track the latest validation
428
+ const currentToken =
429
+ ( customValidationCounterRef.current[ field.id ] || 0 ) + 1;
430
+ customValidationCounterRef.current[ field.id ] = currentToken;
431
+
432
+ updateFieldValidity( setFormValidity, parentFieldId, field.id, {
433
+ custom: {
434
+ type: 'validating',
435
+ message: 'Validating...',
436
+ },
437
+ } );
438
+
439
+ customError
440
+ .then( ( result ) => {
441
+ if (
442
+ customValidationCounterRef.current[ field.id ] !==
443
+ currentToken
444
+ ) {
445
+ return;
446
+ }
447
+
448
+ if ( result === null ) {
449
+ updateFieldValidity(
450
+ setFormValidity,
451
+ parentFieldId,
452
+ field.id,
453
+ {
454
+ custom: {
455
+ type: 'valid',
456
+ message: 'Valid',
457
+ },
458
+ }
459
+ );
460
+ return;
461
+ }
462
+
463
+ if ( typeof result === 'string' ) {
464
+ updateFieldValidity(
465
+ setFormValidity,
466
+ parentFieldId,
467
+ field.id,
468
+ {
469
+ custom: {
470
+ type: 'invalid',
471
+ message: result,
472
+ },
473
+ }
474
+ );
475
+ }
476
+ } )
477
+ .catch( ( error ) => {
478
+ if (
479
+ customValidationCounterRef.current[ field.id ] !==
480
+ currentToken
481
+ ) {
482
+ return;
483
+ }
484
+
485
+ updateFieldValidity(
486
+ setFormValidity,
487
+ parentFieldId,
488
+ field.id,
489
+ {
490
+ custom: {
491
+ type: 'invalid',
492
+ message: error.message,
493
+ },
494
+ }
495
+ );
496
+ } );
497
+
498
+ return;
499
+ }
500
+
501
+ // No errors for this field, remove from errors object
502
+ setFormValidity( ( prev ) => {
503
+ if ( ! prev ) {
504
+ return prev;
505
+ }
506
+
507
+ if ( parentFieldId ) {
508
+ // This field is a child - remove it from parent's children
509
+ const parentField = prev[ parentFieldId ];
510
+ if ( ! parentField?.children ) {
511
+ return prev;
512
+ }
513
+
514
+ const { [ field.id ]: removed, ...restChildren } =
515
+ parentField.children as any;
516
+
517
+ // If no more children, remove the children property
518
+ if ( Object.keys( restChildren ).length === 0 ) {
519
+ const { children, ...restParent } = parentField;
520
+ if ( Object.keys( restParent ).length === 0 ) {
521
+ // Remove parent field entirely if no other validations
522
+ const {
523
+ [ parentFieldId ]: removedParent,
524
+ ...restFields
525
+ } = prev;
526
+ return Object.keys( restFields ).length === 0
527
+ ? undefined
528
+ : restFields;
529
+ }
530
+ return {
531
+ ...prev,
532
+ [ parentFieldId ]: restParent,
533
+ };
534
+ }
535
+
536
+ return {
537
+ ...prev,
538
+ [ parentFieldId ]: {
539
+ ...parentField,
540
+ children: restChildren,
541
+ },
542
+ };
543
+ }
544
+
545
+ // Regular field - remove from top level
546
+ if ( ! prev[ field.id ] ) {
547
+ return prev;
548
+ }
549
+
550
+ const { [ field.id ]: removed, ...rest } = prev;
551
+
552
+ if ( Object.keys( rest ).length === 0 ) {
553
+ return undefined;
554
+ }
555
+
556
+ return rest;
557
+ } );
558
+ } );
559
+ }, [ item, fields, form ] );
560
+
561
+ useEffect( () => {
562
+ validate();
563
+ }, [ validate ] );
564
+
565
+ return {
566
+ validity: formValidity,
567
+ isValid: isFormValid( formValidity ),
568
+ };
569
+ }
570
+
571
+ export default useFormValidity;
package/src/index.ts CHANGED
@@ -2,6 +2,6 @@ export { default as DataViews } from './components/dataviews';
2
2
  export { default as DataViewsPicker } from './components/dataviews-picker';
3
3
  export { default as DataForm } from './components/dataform';
4
4
  export { default as filterSortAndPaginate } from './utils/filter-sort-and-paginate';
5
- export { default as isItemValid } from './utils/is-item-valid';
5
+ export { useFormValidity } from './hooks';
6
6
  export { VIEW_LAYOUTS } from './dataviews-layouts';
7
7
  export type * from './types';