@wordpress/dataviews 11.0.0 → 11.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 (303) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +140 -93
  3. package/build/components/dataviews-filters/filter.js +25 -267
  4. package/build/components/dataviews-filters/filter.js.map +2 -2
  5. package/build/components/dataviews-filters/input-widget.js +1 -4
  6. package/build/components/dataviews-filters/input-widget.js.map +2 -2
  7. package/build/components/dataviews-filters/use-filters.js +3 -3
  8. package/build/components/dataviews-filters/use-filters.js.map +2 -2
  9. package/build/constants.js +5 -142
  10. package/build/constants.js.map +2 -2
  11. package/build/dataform-controls/number.js +2 -1
  12. package/build/dataform-controls/number.js.map +2 -2
  13. package/build/dataform-controls/textarea.js +3 -1
  14. package/build/dataform-controls/textarea.js.map +2 -2
  15. package/build/dataform-controls/utils/get-custom-validity.js +8 -0
  16. package/build/dataform-controls/utils/get-custom-validity.js.map +2 -2
  17. package/build/dataform-controls/utils/validated-input.js +4 -2
  18. package/build/dataform-controls/utils/validated-input.js.map +2 -2
  19. package/build/dataform-controls/utils/validated-number.js +4 -2
  20. package/build/dataform-controls/utils/validated-number.js.map +2 -2
  21. package/build/dataform-layouts/panel/modal.js +14 -5
  22. package/build/dataform-layouts/panel/modal.js.map +2 -2
  23. package/build/dataviews-layouts/grid/composite-grid.js +39 -37
  24. package/build/dataviews-layouts/grid/composite-grid.js.map +3 -3
  25. package/build/dataviews-layouts/table/index.js +8 -3
  26. package/build/dataviews-layouts/table/index.js.map +2 -2
  27. package/build/field-types/array.js +27 -14
  28. package/build/field-types/array.js.map +3 -3
  29. package/build/field-types/boolean.js +13 -10
  30. package/build/field-types/boolean.js.map +3 -3
  31. package/build/field-types/color.js +14 -11
  32. package/build/field-types/color.js.map +3 -3
  33. package/build/field-types/date.js +11 -8
  34. package/build/field-types/date.js.map +3 -3
  35. package/build/field-types/datetime.js +7 -5
  36. package/build/field-types/datetime.js.map +3 -3
  37. package/build/field-types/email.js +20 -11
  38. package/build/field-types/email.js.map +3 -3
  39. package/build/field-types/index.js +12 -14
  40. package/build/field-types/index.js.map +3 -3
  41. package/build/field-types/integer.js +63 -14
  42. package/build/field-types/integer.js.map +3 -3
  43. package/build/field-types/media.js +5 -5
  44. package/build/field-types/media.js.map +2 -2
  45. package/build/field-types/no-type.js +9 -6
  46. package/build/field-types/no-type.js.map +3 -3
  47. package/build/field-types/number.js +51 -15
  48. package/build/field-types/number.js.map +3 -3
  49. package/build/field-types/password.js +13 -5
  50. package/build/field-types/password.js.map +3 -3
  51. package/build/field-types/telephone.js +13 -5
  52. package/build/field-types/telephone.js.map +3 -3
  53. package/build/field-types/text.js +13 -5
  54. package/build/field-types/text.js.map +3 -3
  55. package/build/field-types/url.js +13 -5
  56. package/build/field-types/url.js.map +3 -3
  57. package/build/field-types/utils/get-is-valid.js +89 -0
  58. package/build/field-types/utils/get-is-valid.js.map +7 -0
  59. package/build/field-types/utils/is-valid-elements.js +35 -0
  60. package/build/field-types/utils/is-valid-elements.js.map +7 -0
  61. package/build/field-types/utils/is-valid-max-length.js +36 -0
  62. package/build/field-types/utils/is-valid-max-length.js.map +7 -0
  63. package/build/field-types/utils/is-valid-max.js +36 -0
  64. package/build/field-types/utils/is-valid-max.js.map +7 -0
  65. package/build/field-types/utils/is-valid-min-length.js +36 -0
  66. package/build/field-types/utils/is-valid-min-length.js.map +7 -0
  67. package/build/field-types/utils/is-valid-min.js +36 -0
  68. package/build/field-types/utils/is-valid-min.js.map +7 -0
  69. package/build/field-types/utils/is-valid-pattern.js +41 -0
  70. package/build/field-types/utils/is-valid-pattern.js.map +7 -0
  71. package/build/field-types/utils/is-valid-required-for-array.js +32 -0
  72. package/build/field-types/utils/is-valid-required-for-array.js.map +7 -0
  73. package/build/field-types/utils/is-valid-required-for-bool.js +30 -0
  74. package/build/field-types/utils/is-valid-required-for-bool.js.map +7 -0
  75. package/build/field-types/utils/is-valid-required.js +30 -0
  76. package/build/field-types/utils/is-valid-required.js.map +7 -0
  77. package/build/hooks/use-form-validity.js +52 -102
  78. package/build/hooks/use-form-validity.js.map +2 -2
  79. package/build/types/field-api.js.map +1 -1
  80. package/build/types/private.js.map +1 -1
  81. package/build/utils/filter-sort-and-paginate.js +5 -0
  82. package/build/utils/filter-sort-and-paginate.js.map +3 -3
  83. package/build/utils/operators.js +399 -0
  84. package/build/utils/operators.js.map +7 -0
  85. package/build-module/components/dataviews-filters/filter.js +26 -292
  86. package/build-module/components/dataviews-filters/filter.js.map +2 -2
  87. package/build-module/components/dataviews-filters/input-widget.js +1 -4
  88. package/build-module/components/dataviews-filters/input-widget.js.map +2 -2
  89. package/build-module/components/dataviews-filters/use-filters.js +6 -3
  90. package/build-module/components/dataviews-filters/use-filters.js.map +2 -2
  91. package/build-module/constants.js +5 -139
  92. package/build-module/constants.js.map +2 -2
  93. package/build-module/dataform-controls/number.js +2 -1
  94. package/build-module/dataform-controls/number.js.map +2 -2
  95. package/build-module/dataform-controls/textarea.js +3 -1
  96. package/build-module/dataform-controls/textarea.js.map +2 -2
  97. package/build-module/dataform-controls/utils/get-custom-validity.js +8 -0
  98. package/build-module/dataform-controls/utils/get-custom-validity.js.map +2 -2
  99. package/build-module/dataform-controls/utils/validated-input.js +4 -2
  100. package/build-module/dataform-controls/utils/validated-input.js.map +2 -2
  101. package/build-module/dataform-controls/utils/validated-number.js +4 -2
  102. package/build-module/dataform-controls/utils/validated-number.js.map +2 -2
  103. package/build-module/dataform-layouts/panel/modal.js +14 -5
  104. package/build-module/dataform-layouts/panel/modal.js.map +2 -2
  105. package/build-module/dataviews-layouts/grid/composite-grid.js +40 -38
  106. package/build-module/dataviews-layouts/grid/composite-grid.js.map +3 -3
  107. package/build-module/dataviews-layouts/table/index.js +8 -3
  108. package/build-module/dataviews-layouts/table/index.js.map +2 -2
  109. package/build-module/field-types/array.js +17 -14
  110. package/build-module/field-types/array.js.map +2 -2
  111. package/build-module/field-types/boolean.js +13 -10
  112. package/build-module/field-types/boolean.js.map +2 -2
  113. package/build-module/field-types/color.js +14 -11
  114. package/build-module/field-types/color.js.map +2 -2
  115. package/build-module/field-types/date.js +11 -8
  116. package/build-module/field-types/date.js.map +2 -2
  117. package/build-module/field-types/datetime.js +7 -5
  118. package/build-module/field-types/datetime.js.map +2 -2
  119. package/build-module/field-types/email.js +20 -11
  120. package/build-module/field-types/email.js.map +2 -2
  121. package/build-module/field-types/index.js +12 -14
  122. package/build-module/field-types/index.js.map +2 -2
  123. package/build-module/field-types/integer.js +58 -13
  124. package/build-module/field-types/integer.js.map +2 -2
  125. package/build-module/field-types/media.js +5 -5
  126. package/build-module/field-types/media.js.map +2 -2
  127. package/build-module/field-types/no-type.js +10 -7
  128. package/build-module/field-types/no-type.js.map +2 -2
  129. package/build-module/field-types/number.js +47 -15
  130. package/build-module/field-types/number.js.map +2 -2
  131. package/build-module/field-types/password.js +13 -5
  132. package/build-module/field-types/password.js.map +2 -2
  133. package/build-module/field-types/telephone.js +13 -5
  134. package/build-module/field-types/telephone.js.map +2 -2
  135. package/build-module/field-types/text.js +13 -5
  136. package/build-module/field-types/text.js.map +2 -2
  137. package/build-module/field-types/url.js +13 -5
  138. package/build-module/field-types/url.js.map +2 -2
  139. package/build-module/field-types/utils/get-is-valid.js +68 -0
  140. package/build-module/field-types/utils/get-is-valid.js.map +7 -0
  141. package/build-module/field-types/utils/is-valid-elements.js +14 -0
  142. package/build-module/field-types/utils/is-valid-elements.js.map +7 -0
  143. package/build-module/field-types/utils/is-valid-max-length.js +15 -0
  144. package/build-module/field-types/utils/is-valid-max-length.js.map +7 -0
  145. package/build-module/field-types/utils/is-valid-max.js +15 -0
  146. package/build-module/field-types/utils/is-valid-max.js.map +7 -0
  147. package/build-module/field-types/utils/is-valid-min-length.js +15 -0
  148. package/build-module/field-types/utils/is-valid-min-length.js.map +7 -0
  149. package/build-module/field-types/utils/is-valid-min.js +15 -0
  150. package/build-module/field-types/utils/is-valid-min.js.map +7 -0
  151. package/build-module/field-types/utils/is-valid-pattern.js +20 -0
  152. package/build-module/field-types/utils/is-valid-pattern.js.map +7 -0
  153. package/build-module/field-types/utils/is-valid-required-for-array.js +11 -0
  154. package/build-module/field-types/utils/is-valid-required-for-array.js.map +7 -0
  155. package/build-module/field-types/utils/is-valid-required-for-bool.js +9 -0
  156. package/build-module/field-types/utils/is-valid-required-for-bool.js.map +7 -0
  157. package/build-module/field-types/utils/is-valid-required.js +9 -0
  158. package/build-module/field-types/utils/is-valid-required.js.map +7 -0
  159. package/build-module/hooks/use-form-validity.js +52 -102
  160. package/build-module/hooks/use-form-validity.js.map +2 -2
  161. package/build-module/utils/filter-sort-and-paginate.js +5 -0
  162. package/build-module/utils/filter-sort-and-paginate.js.map +2 -2
  163. package/build-module/utils/operators.js +394 -0
  164. package/build-module/utils/operators.js.map +7 -0
  165. package/build-style/style-rtl.css +13 -80
  166. package/build-style/style.css +13 -80
  167. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  168. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  169. package/build-types/components/dataviews-filters/use-filters.d.ts.map +1 -1
  170. package/build-types/constants.d.ts +6 -12
  171. package/build-types/constants.d.ts.map +1 -1
  172. package/build-types/dataform-controls/number.d.ts.map +1 -1
  173. package/build-types/dataform-controls/textarea.d.ts.map +1 -1
  174. package/build-types/dataform-controls/utils/get-custom-validity.d.ts +2 -2
  175. package/build-types/dataform-controls/utils/get-custom-validity.d.ts.map +1 -1
  176. package/build-types/dataform-controls/utils/validated-input.d.ts.map +1 -1
  177. package/build-types/dataform-controls/utils/validated-number.d.ts.map +1 -1
  178. package/build-types/dataform-layouts/panel/modal.d.ts.map +1 -1
  179. package/build-types/dataviews-layouts/grid/composite-grid.d.ts.map +1 -1
  180. package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
  181. package/build-types/field-types/array.d.ts +9 -2
  182. package/build-types/field-types/array.d.ts.map +1 -1
  183. package/build-types/field-types/boolean.d.ts +9 -2
  184. package/build-types/field-types/boolean.d.ts.map +1 -1
  185. package/build-types/field-types/color.d.ts +9 -2
  186. package/build-types/field-types/color.d.ts.map +1 -1
  187. package/build-types/field-types/date.d.ts +6 -4
  188. package/build-types/field-types/date.d.ts.map +1 -1
  189. package/build-types/field-types/datetime.d.ts +6 -4
  190. package/build-types/field-types/datetime.d.ts.map +1 -1
  191. package/build-types/field-types/email.d.ts +15 -2
  192. package/build-types/field-types/email.d.ts.map +1 -1
  193. package/build-types/field-types/index.d.ts.map +1 -1
  194. package/build-types/field-types/integer.d.ts +17 -4
  195. package/build-types/field-types/integer.d.ts.map +1 -1
  196. package/build-types/field-types/media.d.ts +1 -4
  197. package/build-types/field-types/media.d.ts.map +1 -1
  198. package/build-types/field-types/no-type.d.ts +6 -4
  199. package/build-types/field-types/no-type.d.ts.map +1 -1
  200. package/build-types/field-types/number.d.ts +17 -4
  201. package/build-types/field-types/number.d.ts.map +1 -1
  202. package/build-types/field-types/password.d.ts +12 -4
  203. package/build-types/field-types/password.d.ts.map +1 -1
  204. package/build-types/field-types/telephone.d.ts +12 -4
  205. package/build-types/field-types/telephone.d.ts.map +1 -1
  206. package/build-types/field-types/text.d.ts +12 -4
  207. package/build-types/field-types/text.d.ts.map +1 -1
  208. package/build-types/field-types/url.d.ts +12 -4
  209. package/build-types/field-types/url.d.ts.map +1 -1
  210. package/build-types/field-types/utils/get-is-valid.d.ts +7 -0
  211. package/build-types/field-types/utils/get-is-valid.d.ts.map +1 -0
  212. package/build-types/field-types/utils/is-valid-elements.d.ts +6 -0
  213. package/build-types/field-types/utils/is-valid-elements.d.ts.map +1 -0
  214. package/build-types/field-types/utils/is-valid-max-length.d.ts +6 -0
  215. package/build-types/field-types/utils/is-valid-max-length.d.ts.map +1 -0
  216. package/build-types/field-types/utils/is-valid-max.d.ts +6 -0
  217. package/build-types/field-types/utils/is-valid-max.d.ts.map +1 -0
  218. package/build-types/field-types/utils/is-valid-min-length.d.ts +6 -0
  219. package/build-types/field-types/utils/is-valid-min-length.d.ts.map +1 -0
  220. package/build-types/field-types/utils/is-valid-min.d.ts +6 -0
  221. package/build-types/field-types/utils/is-valid-min.d.ts.map +1 -0
  222. package/build-types/field-types/utils/is-valid-pattern.d.ts +6 -0
  223. package/build-types/field-types/utils/is-valid-pattern.d.ts.map +1 -0
  224. package/build-types/field-types/utils/is-valid-required-for-array.d.ts +6 -0
  225. package/build-types/field-types/utils/is-valid-required-for-array.d.ts.map +1 -0
  226. package/build-types/field-types/utils/is-valid-required-for-bool.d.ts +6 -0
  227. package/build-types/field-types/utils/is-valid-required-for-bool.d.ts.map +1 -0
  228. package/build-types/field-types/utils/is-valid-required.d.ts +6 -0
  229. package/build-types/field-types/utils/is-valid-required.d.ts.map +1 -0
  230. package/build-types/hooks/use-form-validity.d.ts.map +1 -1
  231. package/build-types/stories/dataform.story.d.ts +9 -1
  232. package/build-types/stories/dataform.story.d.ts.map +1 -1
  233. package/build-types/stories/dataviews-picker.story.d.ts.map +1 -1
  234. package/build-types/stories/dataviews.story.d.ts +27 -2
  235. package/build-types/stories/dataviews.story.d.ts.map +1 -1
  236. package/build-types/stories/field-types.story.d.ts +39 -2
  237. package/build-types/stories/field-types.story.d.ts.map +1 -1
  238. package/build-types/types/field-api.d.ts +72 -4
  239. package/build-types/types/field-api.d.ts.map +1 -1
  240. package/build-types/types/private.d.ts +13 -3
  241. package/build-types/types/private.d.ts.map +1 -1
  242. package/build-types/utils/filter-sort-and-paginate.d.ts.map +1 -1
  243. package/build-types/utils/operators.d.ts +16 -0
  244. package/build-types/utils/operators.d.ts.map +1 -0
  245. package/build-wp/index.js +1548 -1452
  246. package/package.json +17 -16
  247. package/src/components/dataviews/style.scss +3 -28
  248. package/src/components/dataviews-filters/filter.tsx +34 -321
  249. package/src/components/dataviews-filters/input-widget.tsx +7 -5
  250. package/src/components/dataviews-filters/use-filters.ts +6 -3
  251. package/src/components/dataviews-footer/style.scss +1 -7
  252. package/src/constants.ts +6 -140
  253. package/src/dataform-controls/number.tsx +3 -3
  254. package/src/dataform-controls/textarea.tsx +7 -1
  255. package/src/dataform-controls/utils/get-custom-validity.ts +10 -2
  256. package/src/dataform-controls/utils/validated-input.tsx +8 -2
  257. package/src/dataform-controls/utils/validated-number.tsx +3 -1
  258. package/src/dataform-layouts/panel/modal.tsx +14 -5
  259. package/src/dataviews-layouts/activity/style.scss +1 -1
  260. package/src/dataviews-layouts/grid/composite-grid.tsx +64 -57
  261. package/src/dataviews-layouts/grid/style.scss +3 -12
  262. package/src/dataviews-layouts/table/index.tsx +9 -3
  263. package/src/dataviews-layouts/table/style.scss +4 -15
  264. package/src/dataviews-layouts/utils/grid-items.scss +1 -9
  265. package/src/field-types/array.tsx +26 -19
  266. package/src/field-types/boolean.tsx +22 -15
  267. package/src/field-types/color.tsx +22 -15
  268. package/src/field-types/date.tsx +14 -11
  269. package/src/field-types/datetime.tsx +6 -4
  270. package/src/field-types/email.tsx +24 -15
  271. package/src/field-types/index.tsx +12 -14
  272. package/src/field-types/integer.tsx +83 -17
  273. package/src/field-types/media.tsx +4 -4
  274. package/src/field-types/no-type.tsx +9 -6
  275. package/src/field-types/number.tsx +82 -16
  276. package/src/field-types/password.tsx +12 -4
  277. package/src/field-types/telephone.tsx +12 -4
  278. package/src/field-types/text.tsx +12 -4
  279. package/src/field-types/url.tsx +12 -4
  280. package/src/field-types/utils/get-is-valid.ts +103 -0
  281. package/src/field-types/utils/is-valid-elements.ts +20 -0
  282. package/src/field-types/utils/is-valid-max-length.ts +23 -0
  283. package/src/field-types/utils/is-valid-max.ts +23 -0
  284. package/src/field-types/utils/is-valid-min-length.ts +23 -0
  285. package/src/field-types/utils/is-valid-min.ts +23 -0
  286. package/src/field-types/utils/is-valid-pattern.ts +29 -0
  287. package/src/field-types/utils/is-valid-required-for-array.ts +18 -0
  288. package/src/field-types/utils/is-valid-required-for-bool.ts +13 -0
  289. package/src/field-types/utils/is-valid-required.ts +13 -0
  290. package/src/hooks/use-form-validity.ts +78 -156
  291. package/src/stories/dataform.story.tsx +126 -30
  292. package/src/stories/dataviews-picker.story.tsx +27 -17
  293. package/src/stories/dataviews.story.tsx +79 -30
  294. package/src/stories/field-types.story.tsx +86 -4
  295. package/src/test/filter-sort-and-paginate.js +2 -1
  296. package/src/test/normalize-fields.ts +87 -11
  297. package/src/test/use-form-validity.ts +796 -31
  298. package/src/types/field-api.ts +90 -4
  299. package/src/types/private.ts +26 -8
  300. package/src/utils/filter-sort-and-paginate.ts +5 -0
  301. package/src/utils/operators.tsx +448 -0
  302. package/tsconfig.json +1 -0
  303. package/tsconfig.tsbuildinfo +1 -1
@@ -23,36 +23,6 @@ import type {
23
23
  NormalizedField,
24
24
  NormalizedFormField,
25
25
  } from '../types';
26
- const isEmptyNullOrUndefined = ( value: any ) =>
27
- [ undefined, '', null ].includes( value );
28
-
29
- const isArrayOrElementsEmptyNullOrUndefined = ( value: any ) => {
30
- return (
31
- ! Array.isArray( value ) ||
32
- value.length === 0 ||
33
- value.every( ( element: any ) => isEmptyNullOrUndefined( element ) )
34
- );
35
- };
36
-
37
- function isInvalidForRequired( fieldType: string | undefined, value: any ) {
38
- if (
39
- ( fieldType === undefined && isEmptyNullOrUndefined( value ) ) ||
40
- ( fieldType === 'text' && isEmptyNullOrUndefined( value ) ) ||
41
- ( fieldType === 'email' && isEmptyNullOrUndefined( value ) ) ||
42
- ( fieldType === 'url' && isEmptyNullOrUndefined( value ) ) ||
43
- ( fieldType === 'telephone' && isEmptyNullOrUndefined( value ) ) ||
44
- ( fieldType === 'password' && isEmptyNullOrUndefined( value ) ) ||
45
- ( fieldType === 'integer' && isEmptyNullOrUndefined( value ) ) ||
46
- ( fieldType === 'number' && isEmptyNullOrUndefined( value ) ) ||
47
- ( fieldType === 'array' &&
48
- isArrayOrElementsEmptyNullOrUndefined( value ) ) ||
49
- ( fieldType === 'boolean' && value !== true )
50
- ) {
51
- return true;
52
- }
53
-
54
- return false;
55
- }
56
26
 
57
27
  function isFormValid( formValidity: FormValidity | undefined ): boolean {
58
28
  if ( ! formValidity ) {
@@ -223,57 +193,12 @@ function handleElementsValidationAsync< Item >(
223
193
  return;
224
194
  }
225
195
 
226
- const validValues = result.map( ( el ) => el.value );
227
- if (
228
- !! formField.field &&
229
- formField.field.type !== 'array' &&
230
- ! validValues.includes( formField.field.getValue( { item } ) )
231
- ) {
232
- setFormValidity( ( prev ) => {
233
- const newFormValidity = setValidityAtPath(
234
- prev,
235
- {
236
- elements: {
237
- type: 'invalid',
238
- message: __(
239
- 'Value must be one of the elements.'
240
- ),
241
- },
242
- },
243
- [ ...path, formField.id ]
244
- );
245
- return newFormValidity;
246
- } );
247
- return;
248
- }
249
-
250
196
  if (
251
- !! formField.field &&
252
- formField.field.type === 'array' &&
253
- ! Array.isArray( formField.field.getValue( { item } ) )
254
- ) {
255
- setFormValidity( ( prev ) => {
256
- const newFormValidity = setValidityAtPath(
257
- prev,
258
- {
259
- elements: {
260
- type: 'invalid',
261
- message: __( 'Value must be an array.' ),
262
- },
263
- },
264
- [ ...path, formField.id ]
265
- );
266
- return newFormValidity;
267
- } );
268
- return;
269
- }
270
-
271
- if (
272
- !! formField.field &&
273
- formField.field.type === 'array' &&
274
- formField.field
275
- .getValue( { item } )
276
- .some( ( v: any ) => ! validValues.includes( v ) )
197
+ formField.field?.isValid.elements &&
198
+ ! formField.field.isValid.elements.validate( item, {
199
+ ...formField.field,
200
+ elements: result,
201
+ } )
277
202
  ) {
278
203
  setFormValidity( ( prev ) => {
279
204
  const newFormValidity = setValidityAtPath(
@@ -434,12 +359,8 @@ function validateFormField< Item >(
434
359
  ): FieldValidity | undefined {
435
360
  // Validate the field: isValid.required
436
361
  if (
437
- !! formField.field &&
438
- formField.field.isValid.required &&
439
- isInvalidForRequired(
440
- formField.field.type,
441
- formField.field.getValue( { item } )
442
- )
362
+ formField.field?.isValid.required &&
363
+ ! formField.field.isValid.required.validate( item, formField.field )
443
364
  ) {
444
365
  return {
445
366
  required: { type: 'invalid' },
@@ -448,82 +369,83 @@ function validateFormField< Item >(
448
369
 
449
370
  // Validate the field: isValid.pattern
450
371
  if (
451
- !! formField.field &&
452
- formField.field.isValid.pattern &&
453
- ( formField.field.type === 'text' ||
454
- formField.field.type === 'email' ||
455
- formField.field.type === 'url' ||
456
- formField.field.type === 'telephone' ||
457
- formField.field.type === 'password' )
372
+ formField.field?.isValid.pattern &&
373
+ ! formField.field.isValid.pattern.validate( item, formField.field )
458
374
  ) {
459
- const value = formField.field.getValue( { item } );
460
- // Only validate pattern if the value is not empty
461
- if ( ! isEmptyNullOrUndefined( value ) ) {
462
- try {
463
- const regex = new RegExp( formField.field.isValid.pattern );
464
- if ( ! regex.test( String( value ) ) ) {
465
- return {
466
- pattern: {
467
- type: 'invalid',
468
- message: __(
469
- 'Value does not match the required pattern.'
470
- ),
471
- },
472
- };
473
- }
474
- } catch ( error ) {
475
- return {
476
- pattern: {
477
- type: 'invalid',
478
- message: __( 'Invalid pattern configuration.' ),
479
- },
480
- };
481
- }
482
- }
375
+ return {
376
+ pattern: {
377
+ type: 'invalid',
378
+ message: __( 'Value does not match the required pattern.' ),
379
+ },
380
+ };
381
+ }
382
+
383
+ // Validate the field: isValid.min
384
+ if (
385
+ formField.field?.isValid.min &&
386
+ ! formField.field.isValid.min.validate( item, formField.field )
387
+ ) {
388
+ return {
389
+ min: {
390
+ type: 'invalid',
391
+ message: __( 'Value is below the minimum.' ),
392
+ },
393
+ };
394
+ }
395
+
396
+ // Validate the field: isValid.max
397
+ if (
398
+ formField.field?.isValid.max &&
399
+ ! formField.field.isValid.max.validate( item, formField.field )
400
+ ) {
401
+ return {
402
+ max: {
403
+ type: 'invalid',
404
+ message: __( 'Value is above the maximum.' ),
405
+ },
406
+ };
407
+ }
408
+
409
+ // Validate the field: isValid.minLength
410
+ if (
411
+ formField.field?.isValid.minLength &&
412
+ ! formField.field.isValid.minLength.validate( item, formField.field )
413
+ ) {
414
+ return {
415
+ minLength: {
416
+ type: 'invalid',
417
+ message: __( 'Value is too short.' ),
418
+ },
419
+ };
420
+ }
421
+
422
+ // Validate the field: isValid.maxLength
423
+ if (
424
+ formField.field?.isValid.maxLength &&
425
+ ! formField.field.isValid.maxLength.validate( item, formField.field )
426
+ ) {
427
+ return {
428
+ maxLength: {
429
+ type: 'invalid',
430
+ message: __( 'Value is too long.' ),
431
+ },
432
+ };
483
433
  }
484
434
 
485
435
  // Validate the field: isValid.elements (static)
486
436
  if (
487
- !! formField.field &&
488
- formField.field.isValid.elements &&
437
+ formField.field?.isValid.elements &&
489
438
  formField.field.hasElements &&
490
439
  ! formField.field.getElements &&
491
- Array.isArray( formField.field.elements )
440
+ Array.isArray( formField.field.elements ) &&
441
+ ! formField.field.isValid.elements.validate( item, formField.field )
492
442
  ) {
493
- const value = formField.field.getValue( { item } );
494
- const validValues = formField.field.elements.map( ( el ) => el.value );
495
-
496
- if (
497
- formField.field.type !== 'array' &&
498
- ! validValues.includes( value )
499
- ) {
500
- return {
501
- elements: {
502
- type: 'invalid',
503
- message: __( 'Value must be one of the elements.' ),
504
- },
505
- };
506
- }
507
-
508
- if ( formField.field.type === 'array' && ! Array.isArray( value ) ) {
509
- return {
510
- elements: {
511
- type: 'invalid',
512
- message: __( 'Value must be an array.' ),
513
- },
514
- };
515
- }
516
- if (
517
- formField.field.type === 'array' &&
518
- value.some( ( v: any ) => ! validValues.includes( v ) )
519
- ) {
520
- return {
521
- elements: {
522
- type: 'invalid',
523
- message: __( 'Value must be one of the elements.' ),
524
- },
525
- };
526
- }
443
+ return {
444
+ elements: {
445
+ type: 'invalid',
446
+ message: __( 'Value must be one of the elements.' ),
447
+ },
448
+ };
527
449
  }
528
450
 
529
451
  // Validate the field: isValid.elements (async)
@@ -549,10 +471,10 @@ function validateFormField< Item >(
549
471
 
550
472
  // Validate the field: isValid.custom (sync)
551
473
  let customError;
552
- if ( !! formField.field ) {
474
+ if ( !! formField.field && formField.field.isValid.custom ) {
553
475
  try {
554
476
  const value = formField.field.getValue( { item } );
555
- customError = formField.field.isValid?.custom?.(
477
+ customError = formField.field.isValid.custom(
556
478
  deepMerge(
557
479
  item,
558
480
  formField.field.setValue( {
@@ -26,9 +26,9 @@ import type {
26
26
  FieldValidity,
27
27
  Form,
28
28
  Layout,
29
+ NormalizedRules,
29
30
  PanelLayout,
30
31
  RegularLayout,
31
- Rules,
32
32
  } from '../types';
33
33
  import { unlock } from '../lock-unlock';
34
34
  import DateControl from '../dataform-controls/date';
@@ -497,7 +497,7 @@ const LayoutPanelComponent = ( {
497
497
  };
498
498
 
499
499
  function getCustomValidity< Item >(
500
- isValid: Rules< Item >,
500
+ isValid: NormalizedRules< Item >,
501
501
  validity: FieldValidity | undefined
502
502
  ) {
503
503
  let customValidity;
@@ -554,11 +554,13 @@ const ValidationComponent = ( {
554
554
  elements,
555
555
  custom,
556
556
  pattern,
557
+ minMax,
557
558
  }: {
558
559
  required: boolean;
559
560
  elements: 'sync' | 'async' | 'none';
560
561
  custom: 'sync' | 'async' | 'none';
561
562
  pattern: boolean;
563
+ minMax: boolean;
562
564
  } ) => {
563
565
  type ValidatedItem = {
564
566
  text: string;
@@ -909,20 +911,63 @@ const ValidationComponent = ( {
909
911
  return undefined;
910
912
  };
911
913
 
914
+ // Helper functions to avoid nested ternary expressions
915
+ const getValidationPlaceholder = (
916
+ basePattern: string,
917
+ baseMinMax: string,
918
+ bothPattern: string
919
+ ) => {
920
+ if ( pattern && minMax ) {
921
+ return bothPattern;
922
+ }
923
+ if ( pattern ) {
924
+ return basePattern;
925
+ }
926
+ if ( minMax ) {
927
+ return baseMinMax;
928
+ }
929
+ return undefined;
930
+ };
931
+
932
+ const getValidationDescription = (
933
+ patternDesc: string,
934
+ minMaxDesc: string,
935
+ bothDesc: string
936
+ ) => {
937
+ if ( pattern && minMax ) {
938
+ return bothDesc;
939
+ }
940
+ if ( pattern ) {
941
+ return patternDesc;
942
+ }
943
+ if ( minMax ) {
944
+ return minMaxDesc;
945
+ }
946
+ return undefined;
947
+ };
948
+
912
949
  return [
913
950
  {
914
951
  id: 'text',
915
952
  type: 'text',
916
953
  label: 'Text',
917
- placeholder: pattern ? 'user_name123' : undefined,
918
- description: pattern
919
- ? 'Must contain only letters, numbers, and underscores'
920
- : undefined,
954
+ placeholder: getValidationPlaceholder(
955
+ 'user_name (alphanumeric+underscore)',
956
+ 'Min 5, max 20 characters',
957
+ 'user_name (5-20 chars, alphanumeric+underscore)'
958
+ ),
959
+ description: getValidationDescription(
960
+ 'Must contain only letters, numbers, and underscores',
961
+ 'Must be between 5 and 20 characters',
962
+ 'Letters, numbers, underscores only AND 5-20 characters'
963
+ ),
921
964
  isValid: {
922
965
  required,
923
966
  elements: elements !== 'none' ? true : false,
924
967
  custom: maybeCustomRule( customTextRule ),
925
968
  pattern: pattern ? '^[a-zA-Z0-9_]+$' : undefined,
969
+ minLength: minMax ? 5 : undefined,
970
+ maxLength: minMax ? 20 : undefined,
926
971
  },
927
972
  },
928
973
  {
@@ -971,61 +1016,89 @@ const ValidationComponent = ( {
971
1016
  type: 'text',
972
1017
  Edit: 'textarea',
973
1018
  label: 'Textarea',
1019
+ placeholder: minMax ? 'Min 10, max 200 characters' : undefined,
1020
+ description: minMax
1021
+ ? 'Must be between 10 and 200 characters'
1022
+ : undefined,
974
1023
  isValid: {
975
1024
  required,
976
1025
  elements: elements !== 'none' ? true : false,
977
1026
  custom: maybeCustomRule( customTextareaRule ),
1027
+ minLength: minMax ? 10 : undefined,
1028
+ maxLength: minMax ? 200 : undefined,
978
1029
  },
979
1030
  },
980
1031
  {
981
1032
  id: 'email',
982
1033
  type: 'email',
983
1034
  label: 'e-mail',
984
- placeholder: pattern ? 'user@company.com' : undefined,
985
- description: pattern
986
- ? 'Email must be from @company.com domain'
987
- : undefined,
1035
+ placeholder: getValidationPlaceholder(
1036
+ 'user@company.com',
1037
+ 'Min 15, max 100 characters',
1038
+ 'user@company.com (15-100 chars)'
1039
+ ),
1040
+ description: getValidationDescription(
1041
+ 'Email must be from @company.com domain',
1042
+ 'Must be between 15 and 100 characters',
1043
+ 'Must be @company.com domain AND 15-100 characters'
1044
+ ),
988
1045
  isValid: {
989
1046
  required,
990
1047
  elements: elements !== 'none' ? true : false,
991
1048
  custom: maybeCustomRule( customEmailRule ),
992
- pattern: pattern ? '^[a-zA-Z0-9]+@company.com$' : undefined,
1049
+ pattern: pattern
1050
+ ? '^[a-zA-Z0-9._%+-]+@company\\.com$'
1051
+ : undefined,
1052
+ minLength: minMax ? 15 : undefined,
1053
+ maxLength: minMax ? 100 : undefined,
993
1054
  },
994
1055
  },
995
1056
  {
996
1057
  id: 'telephone',
997
1058
  type: 'telephone',
998
1059
  label: 'telephone',
999
- placeholder: pattern ? '+1-555-123-4567' : undefined,
1000
- description: pattern
1001
- ? 'US phone format with country code'
1002
- : undefined,
1060
+ placeholder: getValidationPlaceholder(
1061
+ '+1-555-123-4567',
1062
+ 'Min 10, max 20 characters',
1063
+ '+1-555-123-4567 (10-20 chars)'
1064
+ ),
1065
+ description: getValidationDescription(
1066
+ 'US phone format with country code',
1067
+ 'Must be between 10 and 20 characters',
1068
+ 'US format +1-XXX... AND 10-20 characters'
1069
+ ),
1003
1070
  isValid: {
1004
1071
  required,
1005
1072
  elements: elements !== 'none' ? true : false,
1006
1073
  custom: maybeCustomRule( customTelephoneRule ),
1007
- pattern: pattern
1008
- ? '^\\+1-\\d{3}-\\d{3}-\\d{4}$'
1009
- : undefined,
1074
+ pattern: pattern ? '^\\+1-\\d{3}-[0-9-]*$' : undefined,
1075
+ minLength: minMax ? 10 : undefined,
1076
+ maxLength: minMax ? 20 : undefined,
1010
1077
  },
1011
1078
  },
1012
1079
  {
1013
1080
  id: 'url',
1014
1081
  type: 'url',
1015
1082
  label: 'URL',
1016
- placeholder: pattern
1017
- ? 'https://github.com/user/repo'
1018
- : undefined,
1019
- description: pattern
1020
- ? 'Must be a GitHub repository URL'
1021
- : undefined,
1083
+ placeholder: getValidationPlaceholder(
1084
+ 'https://github.com/user/repo',
1085
+ 'Min 25, max 255 characters',
1086
+ 'https://github.com/user/repo (10-255 chars)'
1087
+ ),
1088
+ description: getValidationDescription(
1089
+ 'Must be a GitHub repository URL',
1090
+ 'Must be between 25 and 255 characters',
1091
+ 'GitHub repository URL AND 25-255 characters'
1092
+ ),
1022
1093
  isValid: {
1023
1094
  required,
1024
1095
  elements: elements !== 'none' ? true : false,
1025
1096
  custom: maybeCustomRule( customUrlRule ),
1026
1097
  pattern: pattern
1027
- ? '^https:\\/\\/github\\.com\\/.*'
1098
+ ? '^https:\\/\\/github\\.com\\/.+$'
1028
1099
  : undefined,
1100
+ minLength: minMax ? 25 : undefined,
1101
+ maxLength: minMax ? 255 : undefined,
1029
1102
  },
1030
1103
  },
1031
1104
  {
@@ -1042,20 +1115,28 @@ const ValidationComponent = ( {
1042
1115
  id: 'integer',
1043
1116
  type: 'integer',
1044
1117
  label: 'Integer',
1118
+ placeholder: minMax ? 'Min 10, max 100' : undefined,
1119
+ description: minMax ? 'Must be between 10 and 100' : undefined,
1045
1120
  isValid: {
1046
1121
  required,
1047
1122
  elements: elements !== 'none' ? true : false,
1048
1123
  custom: maybeCustomRule( customIntegerRule ),
1124
+ min: minMax ? 10 : undefined,
1125
+ max: minMax ? 100 : undefined,
1049
1126
  },
1050
1127
  },
1051
1128
  {
1052
1129
  id: 'number',
1053
1130
  type: 'number',
1054
1131
  label: 'Number',
1132
+ placeholder: minMax ? 'Min 10, max 100' : undefined,
1133
+ description: minMax ? 'Must be between 0 and 100' : undefined,
1055
1134
  isValid: {
1056
1135
  required,
1057
1136
  elements: elements !== 'none' ? true : false,
1058
1137
  custom: maybeCustomRule( customNumberRule ),
1138
+ min: minMax ? 10 : undefined,
1139
+ max: minMax ? 100 : undefined,
1059
1140
  },
1060
1141
  },
1061
1142
  {
@@ -1108,14 +1189,23 @@ const ValidationComponent = ( {
1108
1189
  id: 'password',
1109
1190
  type: 'password',
1110
1191
  label: 'Password',
1111
- description: pattern
1112
- ? 'Must have 8 numbers or letters'
1113
- : undefined,
1192
+ placeholder: getValidationPlaceholder(
1193
+ 'Must be 8+ alphanumeric',
1194
+ 'Min 10, max 20 characters',
1195
+ 'abc12345 (10-20 chars alphanumeric)'
1196
+ ),
1197
+ description: getValidationDescription(
1198
+ 'Must contain only letters and numbers (8+ chars)',
1199
+ 'Must be between 10 and 20 characters',
1200
+ 'alphanumeric chars AND 10-20 characters'
1201
+ ),
1114
1202
  isValid: {
1115
1203
  required,
1116
1204
  elements: elements !== 'none' ? true : false,
1117
1205
  custom: maybeCustomRule( customPasswordRule ),
1118
- pattern: pattern ? '^[0-9a-zA-Z]{8}$' : undefined,
1206
+ pattern: pattern ? '^[a-zA-Z0-9]{8,}$' : undefined,
1207
+ minLength: minMax ? 10 : undefined,
1208
+ maxLength: minMax ? 20 : undefined,
1119
1209
  },
1120
1210
  },
1121
1211
  {
@@ -1184,7 +1274,7 @@ const ValidationComponent = ( {
1184
1274
  },
1185
1275
  },
1186
1276
  ];
1187
- }, [ elements, custom, required, getElements ] );
1277
+ }, [ elements, custom, required, pattern, minMax, getElements ] );
1188
1278
 
1189
1279
  const form = useMemo(
1190
1280
  () => ( {
@@ -2121,12 +2211,18 @@ export const Validation = {
2121
2211
  description:
2122
2212
  'Whether or not the pattern validation rule is active.',
2123
2213
  },
2214
+ minMax: {
2215
+ control: { type: 'boolean' },
2216
+ description:
2217
+ 'Whether or not the min/max validation rule is active.',
2218
+ },
2124
2219
  },
2125
2220
  args: {
2126
2221
  required: true,
2127
2222
  elements: 'sync',
2128
2223
  custom: 'sync',
2129
2224
  pattern: false,
2225
+ minMax: false,
2130
2226
  },
2131
2227
  };
2132
2228
 
@@ -265,23 +265,33 @@ export const WithModal = ( {
265
265
  </p>
266
266
  ) }
267
267
  { isModalOpen && (
268
- <Modal
269
- title="Select Items"
270
- onRequestClose={ () => setIsModalOpen( false ) }
271
- isFullScreen={ false }
272
- size="fill"
273
- >
274
- <DataViewsPickerContent
275
- perPageSizes={ perPageSizes }
276
- isMultiselectable={ isMultiselectable }
277
- isGrouped={ isGrouped }
278
- infiniteScrollEnabled={ infiniteScrollEnabled }
279
- actions={ modalActions }
280
- selection={ selectedItems.map( ( item ) =>
281
- String( item.id )
282
- ) }
283
- />
284
- </Modal>
268
+ <>
269
+ <style>{ `
270
+ .components-modal__content {
271
+ padding: 0;
272
+ }
273
+ .components-modal__frame.is-full-screen .components-modal__content {
274
+ margin-bottom: 0;
275
+ }
276
+ ` }</style>
277
+ <Modal
278
+ title="Select Items"
279
+ onRequestClose={ () => setIsModalOpen( false ) }
280
+ isFullScreen={ false }
281
+ size="fill"
282
+ >
283
+ <DataViewsPickerContent
284
+ perPageSizes={ perPageSizes }
285
+ isMultiselectable={ isMultiselectable }
286
+ isGrouped={ isGrouped }
287
+ infiniteScrollEnabled={ infiniteScrollEnabled }
288
+ actions={ modalActions }
289
+ selection={ selectedItems.map( ( item ) =>
290
+ String( item.id )
291
+ ) }
292
+ />
293
+ </Modal>
294
+ </>
285
295
  ) }
286
296
  </>
287
297
  );