@wordpress/dataviews 14.0.0 → 14.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 (249) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +16 -5
  3. package/build/components/dataform-controls/array.cjs +2 -0
  4. package/build/components/dataform-controls/array.cjs.map +2 -2
  5. package/build/components/dataform-controls/checkbox.cjs +3 -1
  6. package/build/components/dataform-controls/checkbox.cjs.map +2 -2
  7. package/build/components/dataform-controls/color.cjs +8 -2
  8. package/build/components/dataform-controls/color.cjs.map +2 -2
  9. package/build/components/dataform-controls/date.cjs +31 -9
  10. package/build/components/dataform-controls/date.cjs.map +3 -3
  11. package/build/components/dataform-controls/datetime.cjs +9 -2
  12. package/build/components/dataform-controls/datetime.cjs.map +3 -3
  13. package/build/components/dataform-controls/password.cjs +4 -1
  14. package/build/components/dataform-controls/password.cjs.map +2 -2
  15. package/build/components/dataform-controls/radio.cjs +3 -1
  16. package/build/components/dataform-controls/radio.cjs.map +2 -2
  17. package/build/components/dataform-controls/select.cjs +3 -1
  18. package/build/components/dataform-controls/select.cjs.map +2 -2
  19. package/build/components/dataform-controls/textarea.cjs +2 -0
  20. package/build/components/dataform-controls/textarea.cjs.map +2 -2
  21. package/build/components/dataform-controls/toggle-group.cjs +3 -1
  22. package/build/components/dataform-controls/toggle-group.cjs.map +2 -2
  23. package/build/components/dataform-controls/toggle.cjs +3 -1
  24. package/build/components/dataform-controls/toggle.cjs.map +2 -2
  25. package/build/components/dataform-controls/utils/relative-date-control.cjs +5 -2
  26. package/build/components/dataform-controls/utils/relative-date-control.cjs.map +2 -2
  27. package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs +48 -0
  28. package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs.map +7 -0
  29. package/build/components/dataform-controls/utils/validated-input.cjs +2 -0
  30. package/build/components/dataform-controls/utils/validated-input.cjs.map +2 -2
  31. package/build/components/dataform-controls/utils/validated-number.cjs +3 -1
  32. package/build/components/dataform-controls/utils/validated-number.cjs.map +2 -2
  33. package/build/components/dataform-layouts/panel/summary-button.cjs +0 -1
  34. package/build/components/dataform-layouts/panel/summary-button.cjs.map +2 -2
  35. package/build/components/dataviews-context/index.cjs.map +1 -1
  36. package/build/components/dataviews-filters/input-widget.cjs +4 -0
  37. package/build/components/dataviews-filters/input-widget.cjs.map +2 -2
  38. package/build/components/dataviews-layouts/picker-grid/index.cjs +1 -4
  39. package/build/components/dataviews-layouts/picker-grid/index.cjs.map +2 -2
  40. package/build/components/dataviews-layouts/picker-table/index.cjs +15 -12
  41. package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
  42. package/build/components/dataviews-layouts/table/index.cjs +0 -1
  43. package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
  44. package/build/components/dataviews-pagination/index.cjs +1 -0
  45. package/build/components/dataviews-pagination/index.cjs.map +2 -2
  46. package/build/dataviews/index.cjs +10 -8
  47. package/build/dataviews/index.cjs.map +2 -2
  48. package/build/dataviews-picker/index.cjs +16 -9
  49. package/build/dataviews-picker/index.cjs.map +2 -2
  50. package/build/field-types/date.cjs +4 -1
  51. package/build/field-types/date.cjs.map +2 -2
  52. package/build/field-types/datetime.cjs +4 -1
  53. package/build/field-types/datetime.cjs.map +2 -2
  54. package/build/field-types/index.cjs +1 -0
  55. package/build/field-types/index.cjs.map +2 -2
  56. package/build/field-types/utils/get-is-valid.cjs +29 -24
  57. package/build/field-types/utils/get-is-valid.cjs.map +2 -2
  58. package/build/field-types/utils/is-valid-date-boundary.cjs +64 -0
  59. package/build/field-types/utils/is-valid-date-boundary.cjs.map +7 -0
  60. package/build/types/dataviews.cjs.map +1 -1
  61. package/build/types/field-api.cjs.map +1 -1
  62. package/build-module/components/dataform-controls/array.mjs +2 -0
  63. package/build-module/components/dataform-controls/array.mjs.map +2 -2
  64. package/build-module/components/dataform-controls/checkbox.mjs +3 -1
  65. package/build-module/components/dataform-controls/checkbox.mjs.map +2 -2
  66. package/build-module/components/dataform-controls/color.mjs +8 -2
  67. package/build-module/components/dataform-controls/color.mjs.map +2 -2
  68. package/build-module/components/dataform-controls/date.mjs +31 -9
  69. package/build-module/components/dataform-controls/date.mjs.map +2 -2
  70. package/build-module/components/dataform-controls/datetime.mjs +9 -2
  71. package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
  72. package/build-module/components/dataform-controls/password.mjs +4 -1
  73. package/build-module/components/dataform-controls/password.mjs.map +2 -2
  74. package/build-module/components/dataform-controls/radio.mjs +3 -1
  75. package/build-module/components/dataform-controls/radio.mjs.map +2 -2
  76. package/build-module/components/dataform-controls/select.mjs +3 -1
  77. package/build-module/components/dataform-controls/select.mjs.map +2 -2
  78. package/build-module/components/dataform-controls/textarea.mjs +2 -0
  79. package/build-module/components/dataform-controls/textarea.mjs.map +2 -2
  80. package/build-module/components/dataform-controls/toggle-group.mjs +3 -1
  81. package/build-module/components/dataform-controls/toggle-group.mjs.map +2 -2
  82. package/build-module/components/dataform-controls/toggle.mjs +3 -1
  83. package/build-module/components/dataform-controls/toggle.mjs.map +2 -2
  84. package/build-module/components/dataform-controls/utils/relative-date-control.mjs +5 -2
  85. package/build-module/components/dataform-controls/utils/relative-date-control.mjs.map +2 -2
  86. package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs +27 -0
  87. package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs.map +7 -0
  88. package/build-module/components/dataform-controls/utils/validated-input.mjs +2 -0
  89. package/build-module/components/dataform-controls/utils/validated-input.mjs.map +2 -2
  90. package/build-module/components/dataform-controls/utils/validated-number.mjs +3 -1
  91. package/build-module/components/dataform-controls/utils/validated-number.mjs.map +2 -2
  92. package/build-module/components/dataform-layouts/panel/summary-button.mjs +0 -1
  93. package/build-module/components/dataform-layouts/panel/summary-button.mjs.map +2 -2
  94. package/build-module/components/dataviews-context/index.mjs.map +1 -1
  95. package/build-module/components/dataviews-filters/input-widget.mjs +4 -0
  96. package/build-module/components/dataviews-filters/input-widget.mjs.map +2 -2
  97. package/build-module/components/dataviews-layouts/picker-grid/index.mjs +1 -4
  98. package/build-module/components/dataviews-layouts/picker-grid/index.mjs.map +2 -2
  99. package/build-module/components/dataviews-layouts/picker-table/index.mjs +15 -12
  100. package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
  101. package/build-module/components/dataviews-layouts/table/index.mjs +0 -1
  102. package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
  103. package/build-module/components/dataviews-pagination/index.mjs +1 -0
  104. package/build-module/components/dataviews-pagination/index.mjs.map +2 -2
  105. package/build-module/dataviews/index.mjs +10 -8
  106. package/build-module/dataviews/index.mjs.map +2 -2
  107. package/build-module/dataviews-picker/index.mjs +16 -9
  108. package/build-module/dataviews-picker/index.mjs.map +2 -2
  109. package/build-module/field-types/date.mjs +4 -1
  110. package/build-module/field-types/date.mjs.map +2 -2
  111. package/build-module/field-types/datetime.mjs +4 -1
  112. package/build-module/field-types/datetime.mjs.map +2 -2
  113. package/build-module/field-types/index.mjs +1 -0
  114. package/build-module/field-types/index.mjs.map +2 -2
  115. package/build-module/field-types/utils/get-is-valid.mjs +29 -24
  116. package/build-module/field-types/utils/get-is-valid.mjs.map +2 -2
  117. package/build-module/field-types/utils/is-valid-date-boundary.mjs +38 -0
  118. package/build-module/field-types/utils/is-valid-date-boundary.mjs.map +7 -0
  119. package/build-style/style-rtl.css +14 -15
  120. package/build-style/style.css +14 -15
  121. package/build-types/components/dataform-controls/array.d.ts.map +1 -1
  122. package/build-types/components/dataform-controls/checkbox.d.ts.map +1 -1
  123. package/build-types/components/dataform-controls/color.d.ts.map +1 -1
  124. package/build-types/components/dataform-controls/date.d.ts.map +1 -1
  125. package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
  126. package/build-types/components/dataform-controls/password.d.ts.map +1 -1
  127. package/build-types/components/dataform-controls/radio.d.ts.map +1 -1
  128. package/build-types/components/dataform-controls/select.d.ts.map +1 -1
  129. package/build-types/components/dataform-controls/textarea.d.ts.map +1 -1
  130. package/build-types/components/dataform-controls/toggle-group.d.ts.map +1 -1
  131. package/build-types/components/dataform-controls/toggle.d.ts.map +1 -1
  132. package/build-types/components/dataform-controls/utils/relative-date-control.d.ts.map +1 -1
  133. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts +16 -0
  134. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts.map +1 -0
  135. package/build-types/components/dataform-controls/utils/validated-input.d.ts.map +1 -1
  136. package/build-types/components/dataform-controls/utils/validated-number.d.ts.map +1 -1
  137. package/build-types/components/dataform-layouts/panel/summary-button.d.ts.map +1 -1
  138. package/build-types/components/dataviews-context/index.d.ts +2 -2
  139. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  140. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  141. package/build-types/components/dataviews-layouts/index.d.ts +6 -6
  142. package/build-types/components/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
  143. package/build-types/components/dataviews-layouts/picker-table/index.d.ts.map +1 -1
  144. package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
  145. package/build-types/components/dataviews-pagination/index.d.ts.map +1 -1
  146. package/build-types/components/dataviews-search/index.d.ts +1 -1
  147. package/build-types/components/dataviews-search/index.d.ts.map +1 -1
  148. package/build-types/constants.d.ts +2 -2
  149. package/build-types/dataform/stories/index.story.d.ts +11 -1
  150. package/build-types/dataform/stories/index.story.d.ts.map +1 -1
  151. package/build-types/dataform/stories/layout-regular.d.ts +2 -1
  152. package/build-types/dataform/stories/layout-regular.d.ts.map +1 -1
  153. package/build-types/dataform/stories/validation.d.ts.map +1 -1
  154. package/build-types/dataviews/index.d.ts +1 -1
  155. package/build-types/dataviews/index.d.ts.map +1 -1
  156. package/build-types/dataviews-picker/index.d.ts +3 -2
  157. package/build-types/dataviews-picker/index.d.ts.map +1 -1
  158. package/build-types/dataviews-picker/stories/index.story.d.ts.map +1 -1
  159. package/build-types/field-types/array.d.ts +1 -1
  160. package/build-types/field-types/array.d.ts.map +1 -1
  161. package/build-types/field-types/boolean.d.ts +1 -1
  162. package/build-types/field-types/boolean.d.ts.map +1 -1
  163. package/build-types/field-types/color.d.ts +1 -1
  164. package/build-types/field-types/color.d.ts.map +1 -1
  165. package/build-types/field-types/date.d.ts +3 -0
  166. package/build-types/field-types/date.d.ts.map +1 -1
  167. package/build-types/field-types/datetime.d.ts +3 -0
  168. package/build-types/field-types/datetime.d.ts.map +1 -1
  169. package/build-types/field-types/email.d.ts +1 -1
  170. package/build-types/field-types/email.d.ts.map +1 -1
  171. package/build-types/field-types/index.d.ts.map +1 -1
  172. package/build-types/field-types/integer.d.ts +1 -1
  173. package/build-types/field-types/integer.d.ts.map +1 -1
  174. package/build-types/field-types/number.d.ts +1 -1
  175. package/build-types/field-types/number.d.ts.map +1 -1
  176. package/build-types/field-types/stories/index.story.d.ts +37 -15
  177. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  178. package/build-types/field-types/utils/get-is-valid.d.ts.map +1 -1
  179. package/build-types/field-types/utils/is-valid-date-boundary.d.ts +7 -0
  180. package/build-types/field-types/utils/is-valid-date-boundary.d.ts.map +1 -0
  181. package/build-types/types/dataviews.d.ts +8 -0
  182. package/build-types/types/dataviews.d.ts.map +1 -1
  183. package/build-types/types/field-api.d.ts +24 -9
  184. package/build-types/types/field-api.d.ts.map +1 -1
  185. package/build-wp/index.js +1141 -990
  186. package/package.json +16 -16
  187. package/src/components/dataform-controls/array.tsx +2 -0
  188. package/src/components/dataform-controls/checkbox.tsx +2 -0
  189. package/src/components/dataform-controls/color.tsx +7 -0
  190. package/src/components/dataform-controls/date.tsx +30 -4
  191. package/src/components/dataform-controls/datetime.tsx +17 -0
  192. package/src/components/dataform-controls/password.tsx +3 -0
  193. package/src/components/dataform-controls/radio.tsx +2 -0
  194. package/src/components/dataform-controls/select.tsx +2 -0
  195. package/src/components/dataform-controls/textarea.tsx +2 -0
  196. package/src/components/dataform-controls/toggle-group.tsx +2 -0
  197. package/src/components/dataform-controls/toggle.tsx +2 -0
  198. package/src/components/dataform-controls/utils/relative-date-control.tsx +3 -0
  199. package/src/components/dataform-controls/utils/use-disabled-date-matchers.ts +48 -0
  200. package/src/components/dataform-controls/utils/validated-input.tsx +2 -0
  201. package/src/components/dataform-controls/utils/validated-number.tsx +2 -0
  202. package/src/components/dataform-layouts/panel/style.scss +4 -5
  203. package/src/components/dataform-layouts/panel/summary-button.tsx +0 -1
  204. package/src/components/dataviews-context/index.ts +2 -2
  205. package/src/components/dataviews-filters/input-widget.tsx +4 -0
  206. package/src/components/dataviews-filters/style.scss +2 -2
  207. package/src/components/dataviews-layouts/activity/style.scss +3 -3
  208. package/src/components/dataviews-layouts/grid/style.scss +1 -1
  209. package/src/components/dataviews-layouts/list/style.scss +1 -1
  210. package/src/components/dataviews-layouts/picker-grid/index.tsx +2 -6
  211. package/src/components/dataviews-layouts/picker-grid/style.scss +1 -1
  212. package/src/components/dataviews-layouts/picker-table/index.tsx +9 -7
  213. package/src/components/dataviews-layouts/picker-table/style.scss +1 -1
  214. package/src/components/dataviews-layouts/table/index.tsx +0 -2
  215. package/src/components/dataviews-pagination/index.tsx +1 -0
  216. package/src/dataform/stories/content.story.tsx +1 -1
  217. package/src/dataform/stories/data-adapter.tsx +6 -6
  218. package/src/dataform/stories/index.story.tsx +7 -0
  219. package/src/dataform/stories/layout-card.tsx +5 -5
  220. package/src/dataform/stories/layout-details.tsx +5 -5
  221. package/src/dataform/stories/layout-panel.tsx +9 -9
  222. package/src/dataform/stories/layout-regular.tsx +31 -10
  223. package/src/dataform/stories/layout-row.tsx +9 -9
  224. package/src/dataform/stories/validation.tsx +25 -10
  225. package/src/dataviews/index.tsx +11 -7
  226. package/src/dataviews/stories/empty.tsx +4 -4
  227. package/src/dataviews/stories/free-composition.tsx +2 -2
  228. package/src/dataviews/stories/infinite-scroll.tsx +4 -4
  229. package/src/dataviews/stories/layout-custom.tsx +1 -1
  230. package/src/dataviews/stories/layout-grid.tsx +1 -1
  231. package/src/dataviews/stories/layout-list.tsx +1 -1
  232. package/src/dataviews/stories/layout-table.tsx +1 -1
  233. package/src/dataviews/stories/minimal-ui.tsx +1 -1
  234. package/src/dataviews/stories/with-card.tsx +4 -4
  235. package/src/dataviews/style.scss +1 -1
  236. package/src/dataviews/test/dataviews.tsx +73 -6
  237. package/src/dataviews-picker/index.tsx +17 -7
  238. package/src/dataviews-picker/stories/index.story.tsx +1 -5
  239. package/src/dataviews-picker/test/dataviews-picker.tsx +79 -2
  240. package/src/field-types/date.tsx +3 -0
  241. package/src/field-types/datetime.tsx +3 -0
  242. package/src/field-types/index.tsx +4 -0
  243. package/src/field-types/stories/index.story.tsx +67 -6
  244. package/src/field-types/test/normalize-fields.ts +44 -0
  245. package/src/field-types/utils/get-is-valid.ts +44 -31
  246. package/src/field-types/utils/is-valid-date-boundary.ts +80 -0
  247. package/src/hooks/test/use-form-validity.ts +479 -0
  248. package/src/types/dataviews.ts +9 -0
  249. package/src/types/field-api.ts +27 -9
@@ -1039,6 +1039,485 @@ describe( 'useFormValidity', () => {
1039
1039
  } );
1040
1040
  } );
1041
1041
 
1042
+ describe( 'isValid.min (date)', () => {
1043
+ const MIN_MESSAGE = {
1044
+ min: {
1045
+ type: 'invalid',
1046
+ message: 'Value is below the minimum.',
1047
+ },
1048
+ };
1049
+
1050
+ it( 'date is valid when value is at min', () => {
1051
+ const item = { id: 1, eventDate: '2026-04-01' };
1052
+ const fields: Field< {} >[] = [
1053
+ {
1054
+ id: 'eventDate',
1055
+ type: 'date',
1056
+ isValid: {
1057
+ min: '2026-04-01',
1058
+ },
1059
+ },
1060
+ ];
1061
+ const form = { fields: [ 'eventDate' ] };
1062
+ const {
1063
+ result: {
1064
+ current: { validity, isValid },
1065
+ },
1066
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1067
+ expect( validity ).toEqual( undefined );
1068
+ expect( isValid ).toBe( true );
1069
+ } );
1070
+
1071
+ it( 'date is invalid when value is before min', () => {
1072
+ const item = { id: 1, eventDate: '2026-03-15' };
1073
+ const fields: Field< {} >[] = [
1074
+ {
1075
+ id: 'eventDate',
1076
+ type: 'date',
1077
+ isValid: {
1078
+ min: '2026-04-01',
1079
+ },
1080
+ },
1081
+ ];
1082
+ const form = { fields: [ 'eventDate' ] };
1083
+ const {
1084
+ result: {
1085
+ current: { validity, isValid },
1086
+ },
1087
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1088
+ expect( validity?.eventDate ).toEqual( MIN_MESSAGE );
1089
+ expect( isValid ).toBe( false );
1090
+ } );
1091
+
1092
+ it( 'date is valid when value is empty and min is defined', () => {
1093
+ const item = { id: 1, eventDate: undefined };
1094
+ const fields: Field< {} >[] = [
1095
+ {
1096
+ id: 'eventDate',
1097
+ type: 'date',
1098
+ isValid: {
1099
+ min: '2026-04-01',
1100
+ },
1101
+ },
1102
+ ];
1103
+ const form = { fields: [ 'eventDate' ] };
1104
+ const {
1105
+ result: {
1106
+ current: { validity, isValid },
1107
+ },
1108
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1109
+ expect( validity ).toEqual( undefined );
1110
+ expect( isValid ).toBe( true );
1111
+ } );
1112
+
1113
+ it( 'date range: from date is validated against min', () => {
1114
+ const item = {
1115
+ id: 1,
1116
+ dateRange: [ '2026-03-15', '2026-04-10' ],
1117
+ };
1118
+ const fields: Field< {} >[] = [
1119
+ {
1120
+ id: 'dateRange',
1121
+ type: 'date',
1122
+ isValid: {
1123
+ min: '2026-04-01',
1124
+ },
1125
+ },
1126
+ ];
1127
+ const form = { fields: [ 'dateRange' ] };
1128
+ const {
1129
+ result: {
1130
+ current: { validity, isValid },
1131
+ },
1132
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1133
+ expect( validity?.dateRange ).toEqual( MIN_MESSAGE );
1134
+ expect( isValid ).toBe( false );
1135
+ } );
1136
+ } );
1137
+
1138
+ describe( 'isValid.max (date)', () => {
1139
+ const MAX_MESSAGE = {
1140
+ max: {
1141
+ type: 'invalid',
1142
+ message: 'Value is above the maximum.',
1143
+ },
1144
+ };
1145
+
1146
+ it( 'date is valid when value is at max', () => {
1147
+ const item = { id: 1, eventDate: '2026-04-20' };
1148
+ const fields: Field< {} >[] = [
1149
+ {
1150
+ id: 'eventDate',
1151
+ type: 'date',
1152
+ isValid: {
1153
+ max: '2026-04-20',
1154
+ },
1155
+ },
1156
+ ];
1157
+ const form = { fields: [ 'eventDate' ] };
1158
+ const {
1159
+ result: {
1160
+ current: { validity, isValid },
1161
+ },
1162
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1163
+ expect( validity ).toEqual( undefined );
1164
+ expect( isValid ).toBe( true );
1165
+ } );
1166
+
1167
+ it( 'date is invalid when value is after max', () => {
1168
+ const item = { id: 1, eventDate: '2026-05-01' };
1169
+ const fields: Field< {} >[] = [
1170
+ {
1171
+ id: 'eventDate',
1172
+ type: 'date',
1173
+ isValid: {
1174
+ max: '2026-04-20',
1175
+ },
1176
+ },
1177
+ ];
1178
+ const form = { fields: [ 'eventDate' ] };
1179
+ const {
1180
+ result: {
1181
+ current: { validity, isValid },
1182
+ },
1183
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1184
+ expect( validity?.eventDate ).toEqual( MAX_MESSAGE );
1185
+ expect( isValid ).toBe( false );
1186
+ } );
1187
+
1188
+ it( 'date range: to date is validated against max', () => {
1189
+ const item = {
1190
+ id: 1,
1191
+ dateRange: [ '2026-04-05', '2026-04-25' ],
1192
+ };
1193
+ const fields: Field< {} >[] = [
1194
+ {
1195
+ id: 'dateRange',
1196
+ type: 'date',
1197
+ isValid: {
1198
+ max: '2026-04-20',
1199
+ },
1200
+ },
1201
+ ];
1202
+ const form = { fields: [ 'dateRange' ] };
1203
+ const {
1204
+ result: {
1205
+ current: { validity, isValid },
1206
+ },
1207
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1208
+ expect( validity?.dateRange ).toEqual( MAX_MESSAGE );
1209
+ expect( isValid ).toBe( false );
1210
+ } );
1211
+ } );
1212
+
1213
+ describe( 'isValid.min (datetime)', () => {
1214
+ const MIN_MESSAGE = {
1215
+ min: {
1216
+ type: 'invalid',
1217
+ message: 'Value is below the minimum.',
1218
+ },
1219
+ };
1220
+
1221
+ it( 'datetime is valid when value is at min', () => {
1222
+ const item = {
1223
+ id: 1,
1224
+ createdAt: '2026-04-01T10:00:00.000Z',
1225
+ };
1226
+ const fields: Field< {} >[] = [
1227
+ {
1228
+ id: 'createdAt',
1229
+ type: 'datetime',
1230
+ isValid: {
1231
+ min: '2026-04-01T10:00:00.000Z',
1232
+ },
1233
+ },
1234
+ ];
1235
+ const form = { fields: [ 'createdAt' ] };
1236
+ const {
1237
+ result: {
1238
+ current: { validity, isValid },
1239
+ },
1240
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1241
+ expect( validity ).toEqual( undefined );
1242
+ expect( isValid ).toBe( true );
1243
+ } );
1244
+
1245
+ it( 'datetime is invalid when value is before min', () => {
1246
+ const item = {
1247
+ id: 1,
1248
+ createdAt: '2026-03-31T23:59:59.000Z',
1249
+ };
1250
+ const fields: Field< {} >[] = [
1251
+ {
1252
+ id: 'createdAt',
1253
+ type: 'datetime',
1254
+ isValid: {
1255
+ min: '2026-04-01T10:00:00.000Z',
1256
+ },
1257
+ },
1258
+ ];
1259
+ const form = { fields: [ 'createdAt' ] };
1260
+ const {
1261
+ result: {
1262
+ current: { validity, isValid },
1263
+ },
1264
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1265
+ expect( validity?.createdAt ).toEqual( MIN_MESSAGE );
1266
+ expect( isValid ).toBe( false );
1267
+ } );
1268
+
1269
+ it( 'datetime is valid when min uses an offset-based ISO string', () => {
1270
+ const item = {
1271
+ id: 1,
1272
+ createdAt: '2026-04-01T09:30:00.000Z',
1273
+ };
1274
+ const fields: Field< {} >[] = [
1275
+ {
1276
+ id: 'createdAt',
1277
+ type: 'datetime',
1278
+ isValid: {
1279
+ min: '2026-04-01T10:00:00+02:00',
1280
+ },
1281
+ },
1282
+ ];
1283
+ const form = { fields: [ 'createdAt' ] };
1284
+ const {
1285
+ result: {
1286
+ current: { validity, isValid },
1287
+ },
1288
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1289
+ expect( validity ).toEqual( undefined );
1290
+ expect( isValid ).toBe( true );
1291
+ } );
1292
+ } );
1293
+
1294
+ describe( 'isValid.max (datetime)', () => {
1295
+ const MAX_MESSAGE = {
1296
+ max: {
1297
+ type: 'invalid',
1298
+ message: 'Value is above the maximum.',
1299
+ },
1300
+ };
1301
+
1302
+ it( 'datetime is valid when value is at max', () => {
1303
+ const item = {
1304
+ id: 1,
1305
+ createdAt: '2026-04-30T23:59:59.000Z',
1306
+ };
1307
+ const fields: Field< {} >[] = [
1308
+ {
1309
+ id: 'createdAt',
1310
+ type: 'datetime',
1311
+ isValid: {
1312
+ max: '2026-04-30T23:59:59.000Z',
1313
+ },
1314
+ },
1315
+ ];
1316
+ const form = { fields: [ 'createdAt' ] };
1317
+ const {
1318
+ result: {
1319
+ current: { validity, isValid },
1320
+ },
1321
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1322
+ expect( validity ).toEqual( undefined );
1323
+ expect( isValid ).toBe( true );
1324
+ } );
1325
+
1326
+ it( 'datetime is invalid when value is after max', () => {
1327
+ const item = {
1328
+ id: 1,
1329
+ createdAt: '2026-05-01T00:00:00.000Z',
1330
+ };
1331
+ const fields: Field< {} >[] = [
1332
+ {
1333
+ id: 'createdAt',
1334
+ type: 'datetime',
1335
+ isValid: {
1336
+ max: '2026-04-30T23:59:59.000Z',
1337
+ },
1338
+ },
1339
+ ];
1340
+ const form = { fields: [ 'createdAt' ] };
1341
+ const {
1342
+ result: {
1343
+ current: { validity, isValid },
1344
+ },
1345
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1346
+ expect( validity?.createdAt ).toEqual( MAX_MESSAGE );
1347
+ expect( isValid ).toBe( false );
1348
+ } );
1349
+
1350
+ it( 'datetime is invalid when max uses an offset-based ISO string', () => {
1351
+ const item = {
1352
+ id: 1,
1353
+ createdAt: '2026-04-01T09:30:00.000Z',
1354
+ };
1355
+ const fields: Field< {} >[] = [
1356
+ {
1357
+ id: 'createdAt',
1358
+ type: 'datetime',
1359
+ isValid: {
1360
+ max: '2026-04-01T10:00:00+02:00',
1361
+ },
1362
+ },
1363
+ ];
1364
+ const form = { fields: [ 'createdAt' ] };
1365
+ const {
1366
+ result: {
1367
+ current: { validity, isValid },
1368
+ },
1369
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1370
+ expect( validity?.createdAt ).toEqual( MAX_MESSAGE );
1371
+ expect( isValid ).toBe( false );
1372
+ } );
1373
+
1374
+ it( 'datetime is invalid when value cannot be parsed', () => {
1375
+ const item = {
1376
+ id: 1,
1377
+ createdAt: 'not-a-date',
1378
+ };
1379
+ const fields: Field< {} >[] = [
1380
+ {
1381
+ id: 'createdAt',
1382
+ type: 'datetime',
1383
+ isValid: {
1384
+ max: '2026-04-30T23:59:59.000Z',
1385
+ },
1386
+ },
1387
+ ];
1388
+ const form = { fields: [ 'createdAt' ] };
1389
+ const {
1390
+ result: {
1391
+ current: { validity, isValid },
1392
+ },
1393
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1394
+ expect( validity?.createdAt ).toEqual( MAX_MESSAGE );
1395
+ expect( isValid ).toBe( false );
1396
+ } );
1397
+ } );
1398
+
1399
+ describe( 'isValid combined min and max (date)', () => {
1400
+ it( 'date is valid when value is within range', () => {
1401
+ const item = { id: 1, eventDate: '2026-04-10' };
1402
+ const fields: Field< {} >[] = [
1403
+ {
1404
+ id: 'eventDate',
1405
+ type: 'date',
1406
+ isValid: {
1407
+ min: '2026-04-01',
1408
+ max: '2026-04-30',
1409
+ },
1410
+ },
1411
+ ];
1412
+ const form = { fields: [ 'eventDate' ] };
1413
+ const {
1414
+ result: {
1415
+ current: { validity, isValid },
1416
+ },
1417
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1418
+ expect( validity ).toEqual( undefined );
1419
+ expect( isValid ).toBe( true );
1420
+ } );
1421
+
1422
+ it( 'date is invalid when value is below min with both min and max', () => {
1423
+ const item = { id: 1, eventDate: '2026-03-15' };
1424
+ const fields: Field< {} >[] = [
1425
+ {
1426
+ id: 'eventDate',
1427
+ type: 'date',
1428
+ isValid: {
1429
+ min: '2026-04-01',
1430
+ max: '2026-04-30',
1431
+ },
1432
+ },
1433
+ ];
1434
+ const form = { fields: [ 'eventDate' ] };
1435
+ const {
1436
+ result: {
1437
+ current: { validity, isValid },
1438
+ },
1439
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1440
+ expect( validity?.eventDate ).toEqual( {
1441
+ min: {
1442
+ type: 'invalid',
1443
+ message: 'Value is below the minimum.',
1444
+ },
1445
+ } );
1446
+ expect( isValid ).toBe( false );
1447
+ } );
1448
+
1449
+ it( 'date is invalid when value is above max with both min and max', () => {
1450
+ const item = { id: 1, eventDate: '2026-05-15' };
1451
+ const fields: Field< {} >[] = [
1452
+ {
1453
+ id: 'eventDate',
1454
+ type: 'date',
1455
+ isValid: {
1456
+ min: '2026-04-01',
1457
+ max: '2026-04-30',
1458
+ },
1459
+ },
1460
+ ];
1461
+ const form = { fields: [ 'eventDate' ] };
1462
+ const {
1463
+ result: {
1464
+ current: { validity, isValid },
1465
+ },
1466
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1467
+ expect( validity?.eventDate ).toEqual( {
1468
+ max: {
1469
+ type: 'invalid',
1470
+ message: 'Value is above the maximum.',
1471
+ },
1472
+ } );
1473
+ expect( isValid ).toBe( false );
1474
+ } );
1475
+ } );
1476
+
1477
+ describe( 'isValid empty array (date)', () => {
1478
+ it( 'empty array is valid for min validation', () => {
1479
+ const item = { id: 1, dateRange: [] as string[] };
1480
+ const fields: Field< {} >[] = [
1481
+ {
1482
+ id: 'dateRange',
1483
+ type: 'date',
1484
+ isValid: {
1485
+ min: '2026-04-01',
1486
+ },
1487
+ },
1488
+ ];
1489
+ const form = { fields: [ 'dateRange' ] };
1490
+ const {
1491
+ result: {
1492
+ current: { validity, isValid },
1493
+ },
1494
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1495
+ expect( validity ).toEqual( undefined );
1496
+ expect( isValid ).toBe( true );
1497
+ } );
1498
+
1499
+ it( 'empty array is valid for max validation', () => {
1500
+ const item = { id: 1, dateRange: [] as string[] };
1501
+ const fields: Field< {} >[] = [
1502
+ {
1503
+ id: 'dateRange',
1504
+ type: 'date',
1505
+ isValid: {
1506
+ max: '2026-04-30',
1507
+ },
1508
+ },
1509
+ ];
1510
+ const form = { fields: [ 'dateRange' ] };
1511
+ const {
1512
+ result: {
1513
+ current: { validity, isValid },
1514
+ },
1515
+ } = renderHook( () => useFormValidity( item, fields, form ) );
1516
+ expect( validity ).toEqual( undefined );
1517
+ expect( isValid ).toBe( true );
1518
+ } );
1519
+ } );
1520
+
1042
1521
  describe( 'isValid.minLength', () => {
1043
1522
  const MIN_LENGTH_MESSAGE = {
1044
1523
  minLength: {
@@ -527,6 +527,15 @@ export type ViewPickerProps< Item > =
527
527
  | ViewPickerTableProps< Item >;
528
528
 
529
529
  export interface SupportedLayouts {
530
+ list?: Omit< ViewList, 'type' > | true;
531
+ grid?: Omit< ViewGrid, 'type' > | true;
532
+ table?: Omit< ViewTable, 'type' > | true;
533
+ activity?: Omit< ViewActivity, 'type' > | true;
534
+ pickerGrid?: Omit< ViewPickerGrid, 'type' > | true;
535
+ pickerTable?: Omit< ViewPickerTable, 'type' > | true;
536
+ }
537
+
538
+ export interface NormalizedSupportedLayouts {
530
539
  list?: Omit< ViewList, 'type' >;
531
540
  grid?: Omit< ViewGrid, 'type' >;
532
541
  table?: Omit< ViewTable, 'type' >;
@@ -82,8 +82,8 @@ export type Rules< Item > = {
82
82
  pattern?: string;
83
83
  minLength?: number;
84
84
  maxLength?: number;
85
- min?: number;
86
- max?: number;
85
+ min?: number | string;
86
+ max?: number | string;
87
87
  custom?:
88
88
  | ( ( item: Item, field: NormalizedField< Item > ) => null | string )
89
89
  | ( (
@@ -125,8 +125,8 @@ export type NormalizedRules< Item > = {
125
125
  pattern?: NormalizedRule< Item, string >;
126
126
  minLength?: NormalizedRule< Item, number >;
127
127
  maxLength?: NormalizedRule< Item, number >;
128
- min?: NormalizedRule< Item, number >;
129
- max?: NormalizedRule< Item, number >;
128
+ min?: NormalizedRule< Item, number > | NormalizedRule< Item, string >;
129
+ max?: NormalizedRule< Item, number > | NormalizedRule< Item, string >;
130
130
  custom?: CustomValidator< Item >;
131
131
  };
132
132
 
@@ -184,12 +184,9 @@ export type EditConfig =
184
184
  | EditConfigDatetime
185
185
  | EditConfigGeneric;
186
186
 
187
- /**
188
- * A dataview field for a specific property of a data type.
189
- */
190
187
  export type Field< Item > = {
191
188
  /**
192
- * Type of the fields.
189
+ * Type of the field.
193
190
  */
194
191
  type?: FieldTypeName;
195
192
 
@@ -235,7 +232,12 @@ export type Field< Item > = {
235
232
  sort?: ( a: Item, b: Item, direction: SortDirection ) => number;
236
233
 
237
234
  /**
238
- * Callback used to validate the field.
235
+ * Validation config for the field.
236
+ *
237
+ * Range rules are normalized according to `type`:
238
+ * - `'integer' | 'number'`: `min`/`max` accept `number`
239
+ * - `'date' | 'datetime'`: `min`/`max` accept `string`
240
+ * - all other field types ignore `min`/`max`
239
241
  */
240
242
  isValid?: Rules< Item >;
241
243
 
@@ -244,6 +246,18 @@ export type Field< Item > = {
244
246
  */
245
247
  isVisible?: ( item: Item ) => boolean;
246
248
 
249
+ /**
250
+ * Whether a field should be disabled.
251
+ * Can be a boolean or a callback receiving the current item and field.
252
+ * Defaults to false.
253
+ */
254
+ isDisabled?:
255
+ | boolean
256
+ | ( ( args: {
257
+ item: Item;
258
+ field: NormalizedField< Item >;
259
+ } ) => boolean );
260
+
247
261
  /**
248
262
  * Whether the field is sortable.
249
263
  */
@@ -380,6 +394,10 @@ export type NormalizedField< Item > = Omit<
380
394
  filterBy: Required< FilterByConfig > | false;
381
395
  filter: FilterOperatorMap< Item >;
382
396
  readOnly: boolean;
397
+ isDisabled: ( args: {
398
+ item: Item;
399
+ field: NormalizedField< Item >;
400
+ } ) => boolean;
383
401
  format:
384
402
  | {}
385
403
  | Required< FormatDate >