@wordpress/dataviews 9.0.1-next.6f42e1382.0 → 9.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 (286) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/README.md +112 -15
  3. package/build/components/dataviews/index.js +4 -6
  4. package/build/components/dataviews/index.js.map +1 -1
  5. package/build/components/dataviews-filters/filters-toggled.js +32 -0
  6. package/build/components/dataviews-filters/filters-toggled.js.map +1 -0
  7. package/build/components/dataviews-filters/filters.js +73 -0
  8. package/build/components/dataviews-filters/filters.js.map +1 -0
  9. package/build/components/dataviews-filters/index.js +26 -190
  10. package/build/components/dataviews-filters/index.js.map +1 -1
  11. package/build/components/dataviews-filters/input-widget.js +48 -4
  12. package/build/components/dataviews-filters/input-widget.js.map +1 -1
  13. package/build/components/dataviews-filters/toggle.js +99 -0
  14. package/build/components/dataviews-filters/toggle.js.map +1 -0
  15. package/build/components/dataviews-filters/use-filters.js +63 -0
  16. package/build/components/dataviews-filters/use-filters.js.map +1 -0
  17. package/build/components/dataviews-picker/index.js +4 -6
  18. package/build/components/dataviews-picker/index.js.map +1 -1
  19. package/build/components/dataviews-view-config/index.js +22 -3
  20. package/build/components/dataviews-view-config/index.js.map +1 -1
  21. package/build/dataform-controls/array.js +117 -29
  22. package/build/dataform-controls/array.js.map +1 -1
  23. package/build/dataform-controls/checkbox.js +31 -20
  24. package/build/dataform-controls/checkbox.js.map +1 -1
  25. package/build/dataform-controls/color.js +29 -24
  26. package/build/dataform-controls/color.js.map +1 -1
  27. package/build/dataform-controls/date.js +32 -24
  28. package/build/dataform-controls/date.js.map +1 -1
  29. package/build/dataform-controls/datetime.js +133 -19
  30. package/build/dataform-controls/datetime.js.map +1 -1
  31. package/build/dataform-controls/email.js +7 -1
  32. package/build/dataform-controls/email.js.map +1 -1
  33. package/build/dataform-controls/index.js +25 -0
  34. package/build/dataform-controls/index.js.map +1 -1
  35. package/build/dataform-controls/integer.js +7 -106
  36. package/build/dataform-controls/integer.js.map +1 -1
  37. package/build/dataform-controls/number.js +21 -0
  38. package/build/dataform-controls/number.js.map +1 -0
  39. package/build/dataform-controls/radio.js +42 -9
  40. package/build/dataform-controls/radio.js.map +1 -1
  41. package/build/dataform-controls/relative-date-control.js +6 -10
  42. package/build/dataform-controls/relative-date-control.js.map +1 -1
  43. package/build/dataform-controls/select.js +41 -10
  44. package/build/dataform-controls/select.js.map +1 -1
  45. package/build/dataform-controls/telephone.js +7 -1
  46. package/build/dataform-controls/telephone.js.map +1 -1
  47. package/build/dataform-controls/text.js +14 -2
  48. package/build/dataform-controls/text.js.map +1 -1
  49. package/build/dataform-controls/textarea.js +33 -20
  50. package/build/dataform-controls/textarea.js.map +1 -1
  51. package/build/dataform-controls/toggle-group.js +36 -6
  52. package/build/dataform-controls/toggle-group.js.map +1 -1
  53. package/build/dataform-controls/toggle.js +33 -22
  54. package/build/dataform-controls/toggle.js.map +1 -1
  55. package/build/dataform-controls/url.js +7 -1
  56. package/build/dataform-controls/url.js.map +1 -1
  57. package/build/dataform-controls/utils/validated-input.js +34 -32
  58. package/build/dataform-controls/utils/validated-input.js.map +1 -1
  59. package/build/dataform-controls/utils/validated-number.js +146 -0
  60. package/build/dataform-controls/utils/validated-number.js.map +1 -0
  61. package/build/dataforms-layouts/panel/dropdown.js +10 -14
  62. package/build/dataforms-layouts/panel/dropdown.js.map +1 -1
  63. package/build/dataforms-layouts/panel/index.js +24 -11
  64. package/build/dataforms-layouts/panel/index.js.map +1 -1
  65. package/build/dataforms-layouts/panel/modal.js +22 -27
  66. package/build/dataforms-layouts/panel/modal.js.map +1 -1
  67. package/build/dataforms-layouts/panel/summary-button.js +67 -0
  68. package/build/dataforms-layouts/panel/summary-button.js.map +1 -0
  69. package/build/dataviews-layouts/picker-grid/index.js +4 -1
  70. package/build/dataviews-layouts/picker-grid/index.js.map +1 -1
  71. package/build/field-types/array.js +0 -6
  72. package/build/field-types/array.js.map +1 -1
  73. package/build/field-types/index.js +4 -0
  74. package/build/field-types/index.js.map +1 -1
  75. package/build/field-types/number.js +71 -0
  76. package/build/field-types/number.js.map +1 -0
  77. package/build/index.js +7 -0
  78. package/build/index.js.map +1 -1
  79. package/build/normalize-fields.js +17 -0
  80. package/build/normalize-fields.js.map +1 -1
  81. package/build/types.js.map +1 -1
  82. package/build/validation.js +18 -1
  83. package/build/validation.js.map +1 -1
  84. package/build-module/components/dataviews/index.js +5 -7
  85. package/build-module/components/dataviews/index.js.map +1 -1
  86. package/build-module/components/dataviews-filters/filters-toggled.js +24 -0
  87. package/build-module/components/dataviews-filters/filters-toggled.js.map +1 -0
  88. package/build-module/components/dataviews-filters/filters.js +65 -0
  89. package/build-module/components/dataviews-filters/filters.js.map +1 -0
  90. package/build-module/components/dataviews-filters/index.js +4 -186
  91. package/build-module/components/dataviews-filters/index.js.map +1 -1
  92. package/build-module/components/dataviews-filters/input-widget.js +48 -4
  93. package/build-module/components/dataviews-filters/input-widget.js.map +1 -1
  94. package/build-module/components/dataviews-filters/toggle.js +91 -0
  95. package/build-module/components/dataviews-filters/toggle.js.map +1 -0
  96. package/build-module/components/dataviews-filters/use-filters.js +56 -0
  97. package/build-module/components/dataviews-filters/use-filters.js.map +1 -0
  98. package/build-module/components/dataviews-picker/index.js +5 -7
  99. package/build-module/components/dataviews-picker/index.js.map +1 -1
  100. package/build-module/components/dataviews-view-config/index.js +22 -3
  101. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  102. package/build-module/dataform-controls/array.js +120 -32
  103. package/build-module/dataform-controls/array.js.map +1 -1
  104. package/build-module/dataform-controls/checkbox.js +31 -21
  105. package/build-module/dataform-controls/checkbox.js.map +1 -1
  106. package/build-module/dataform-controls/color.js +28 -24
  107. package/build-module/dataform-controls/color.js.map +1 -1
  108. package/build-module/dataform-controls/date.js +32 -24
  109. package/build-module/dataform-controls/date.js.map +1 -1
  110. package/build-module/dataform-controls/datetime.js +135 -21
  111. package/build-module/dataform-controls/datetime.js.map +1 -1
  112. package/build-module/dataform-controls/email.js +7 -1
  113. package/build-module/dataform-controls/email.js.map +1 -1
  114. package/build-module/dataform-controls/index.js +25 -0
  115. package/build-module/dataform-controls/index.js.map +1 -1
  116. package/build-module/dataform-controls/integer.js +7 -106
  117. package/build-module/dataform-controls/integer.js.map +1 -1
  118. package/build-module/dataform-controls/number.js +14 -0
  119. package/build-module/dataform-controls/number.js.map +1 -0
  120. package/build-module/dataform-controls/radio.js +44 -11
  121. package/build-module/dataform-controls/radio.js.map +1 -1
  122. package/build-module/dataform-controls/relative-date-control.js +6 -10
  123. package/build-module/dataform-controls/relative-date-control.js.map +1 -1
  124. package/build-module/dataform-controls/select.js +43 -12
  125. package/build-module/dataform-controls/select.js.map +1 -1
  126. package/build-module/dataform-controls/telephone.js +7 -1
  127. package/build-module/dataform-controls/telephone.js.map +1 -1
  128. package/build-module/dataform-controls/text.js +14 -2
  129. package/build-module/dataform-controls/text.js.map +1 -1
  130. package/build-module/dataform-controls/textarea.js +32 -20
  131. package/build-module/dataform-controls/textarea.js.map +1 -1
  132. package/build-module/dataform-controls/toggle-group.js +38 -8
  133. package/build-module/dataform-controls/toggle-group.js.map +1 -1
  134. package/build-module/dataform-controls/toggle.js +33 -23
  135. package/build-module/dataform-controls/toggle.js.map +1 -1
  136. package/build-module/dataform-controls/url.js +7 -1
  137. package/build-module/dataform-controls/url.js.map +1 -1
  138. package/build-module/dataform-controls/utils/validated-input.js +34 -33
  139. package/build-module/dataform-controls/utils/validated-input.js.map +1 -1
  140. package/build-module/dataform-controls/utils/validated-number.js +138 -0
  141. package/build-module/dataform-controls/utils/validated-number.js.map +1 -0
  142. package/build-module/dataforms-layouts/panel/dropdown.js +10 -15
  143. package/build-module/dataforms-layouts/panel/dropdown.js.map +1 -1
  144. package/build-module/dataforms-layouts/panel/index.js +24 -11
  145. package/build-module/dataforms-layouts/panel/index.js.map +1 -1
  146. package/build-module/dataforms-layouts/panel/modal.js +22 -28
  147. package/build-module/dataforms-layouts/panel/modal.js.map +1 -1
  148. package/build-module/dataforms-layouts/panel/summary-button.js +60 -0
  149. package/build-module/dataforms-layouts/panel/summary-button.js.map +1 -0
  150. package/build-module/dataviews-layouts/picker-grid/index.js +4 -1
  151. package/build-module/dataviews-layouts/picker-grid/index.js.map +1 -1
  152. package/build-module/field-types/array.js +0 -6
  153. package/build-module/field-types/array.js.map +1 -1
  154. package/build-module/field-types/index.js +4 -0
  155. package/build-module/field-types/index.js.map +1 -1
  156. package/build-module/field-types/number.js +65 -0
  157. package/build-module/field-types/number.js.map +1 -0
  158. package/build-module/index.js +1 -0
  159. package/build-module/index.js.map +1 -1
  160. package/build-module/normalize-fields.js +15 -0
  161. package/build-module/normalize-fields.js.map +1 -1
  162. package/build-module/types.js.map +1 -1
  163. package/build-module/validation.js +18 -1
  164. package/build-module/validation.js.map +1 -1
  165. package/build-types/components/dataform/stories/index.story.d.ts +3 -0
  166. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  167. package/build-types/components/dataviews/index.d.ts +3 -2
  168. package/build-types/components/dataviews/index.d.ts.map +1 -1
  169. package/build-types/components/dataviews/stories/fixtures.d.ts +4 -2
  170. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  171. package/build-types/components/dataviews-filters/filters-toggled.d.ts +5 -0
  172. package/build-types/components/dataviews-filters/filters-toggled.d.ts.map +1 -0
  173. package/build-types/components/dataviews-filters/filters.d.ts +6 -0
  174. package/build-types/components/dataviews-filters/filters.d.ts.map +1 -0
  175. package/build-types/components/dataviews-filters/index.d.ts +4 -8
  176. package/build-types/components/dataviews-filters/index.d.ts.map +1 -1
  177. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  178. package/build-types/components/dataviews-filters/toggle.d.ts +3 -0
  179. package/build-types/components/dataviews-filters/toggle.d.ts.map +1 -0
  180. package/build-types/components/dataviews-filters/use-filters.d.ts +4 -0
  181. package/build-types/components/dataviews-filters/use-filters.d.ts.map +1 -0
  182. package/build-types/components/dataviews-picker/index.d.ts +3 -2
  183. package/build-types/components/dataviews-picker/index.d.ts.map +1 -1
  184. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  185. package/build-types/dataform-controls/array.d.ts.map +1 -1
  186. package/build-types/dataform-controls/checkbox.d.ts.map +1 -1
  187. package/build-types/dataform-controls/color.d.ts.map +1 -1
  188. package/build-types/dataform-controls/date.d.ts.map +1 -1
  189. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  190. package/build-types/dataform-controls/email.d.ts.map +1 -1
  191. package/build-types/dataform-controls/index.d.ts +1 -1
  192. package/build-types/dataform-controls/index.d.ts.map +1 -1
  193. package/build-types/dataform-controls/integer.d.ts +4 -1
  194. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  195. package/build-types/dataform-controls/number.d.ts +6 -0
  196. package/build-types/dataform-controls/number.d.ts.map +1 -0
  197. package/build-types/dataform-controls/radio.d.ts.map +1 -1
  198. package/build-types/dataform-controls/relative-date-control.d.ts +6 -5
  199. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -1
  200. package/build-types/dataform-controls/select.d.ts.map +1 -1
  201. package/build-types/dataform-controls/telephone.d.ts.map +1 -1
  202. package/build-types/dataform-controls/text.d.ts +1 -1
  203. package/build-types/dataform-controls/text.d.ts.map +1 -1
  204. package/build-types/dataform-controls/textarea.d.ts +1 -1
  205. package/build-types/dataform-controls/textarea.d.ts.map +1 -1
  206. package/build-types/dataform-controls/toggle-group.d.ts.map +1 -1
  207. package/build-types/dataform-controls/toggle.d.ts.map +1 -1
  208. package/build-types/dataform-controls/url.d.ts.map +1 -1
  209. package/build-types/dataform-controls/utils/validated-input.d.ts +4 -4
  210. package/build-types/dataform-controls/utils/validated-input.d.ts.map +1 -1
  211. package/build-types/dataform-controls/utils/validated-number.d.ts +9 -0
  212. package/build-types/dataform-controls/utils/validated-number.d.ts.map +1 -0
  213. package/build-types/dataforms-layouts/panel/dropdown.d.ts +2 -1
  214. package/build-types/dataforms-layouts/panel/dropdown.d.ts.map +1 -1
  215. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
  216. package/build-types/dataforms-layouts/panel/modal.d.ts +2 -1
  217. package/build-types/dataforms-layouts/panel/modal.d.ts.map +1 -1
  218. package/build-types/dataforms-layouts/panel/summary-button.d.ts +15 -0
  219. package/build-types/dataforms-layouts/panel/summary-button.d.ts.map +1 -0
  220. package/build-types/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
  221. package/build-types/field-types/array.d.ts.map +1 -1
  222. package/build-types/field-types/index.d.ts.map +1 -1
  223. package/build-types/field-types/number.d.ts +20 -0
  224. package/build-types/field-types/number.d.ts.map +1 -0
  225. package/build-types/field-types/stories/index.story.d.ts +106 -57
  226. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  227. package/build-types/index.d.ts +1 -0
  228. package/build-types/index.d.ts.map +1 -1
  229. package/build-types/normalize-fields.d.ts +3 -0
  230. package/build-types/normalize-fields.d.ts.map +1 -1
  231. package/build-types/types.d.ts +69 -5
  232. package/build-types/types.d.ts.map +1 -1
  233. package/build-types/validation.d.ts.map +1 -1
  234. package/build-wp/index.js +2354 -1717
  235. package/package.json +16 -15
  236. package/src/components/dataform/stories/index.story.tsx +528 -8
  237. package/src/components/dataviews/index.tsx +8 -14
  238. package/src/components/dataviews/stories/fixtures.tsx +99 -41
  239. package/src/components/dataviews/stories/index.story.tsx +2 -2
  240. package/src/components/dataviews-filters/filters-toggled.tsx +20 -0
  241. package/src/components/dataviews-filters/filters.tsx +73 -0
  242. package/src/components/dataviews-filters/index.tsx +4 -246
  243. package/src/components/dataviews-filters/input-widget.tsx +44 -5
  244. package/src/components/dataviews-filters/toggle.tsx +118 -0
  245. package/src/components/dataviews-filters/use-filters.ts +73 -0
  246. package/src/components/dataviews-picker/index.tsx +8 -14
  247. package/src/components/dataviews-picker/stories/index.story.tsx +1 -1
  248. package/src/components/dataviews-view-config/index.tsx +18 -3
  249. package/src/dataform-controls/array.tsx +139 -44
  250. package/src/dataform-controls/checkbox.tsx +41 -24
  251. package/src/dataform-controls/color.tsx +33 -24
  252. package/src/dataform-controls/date.tsx +47 -21
  253. package/src/dataform-controls/datetime.tsx +171 -23
  254. package/src/dataform-controls/email.tsx +9 -1
  255. package/src/dataform-controls/index.tsx +28 -0
  256. package/src/dataform-controls/integer.tsx +3 -146
  257. package/src/dataform-controls/number.tsx +10 -0
  258. package/src/dataform-controls/radio.tsx +53 -11
  259. package/src/dataform-controls/relative-date-control.tsx +11 -10
  260. package/src/dataform-controls/select.tsx +53 -10
  261. package/src/dataform-controls/telephone.tsx +9 -1
  262. package/src/dataform-controls/text.tsx +18 -1
  263. package/src/dataform-controls/textarea.tsx +38 -24
  264. package/src/dataform-controls/toggle-group.tsx +50 -10
  265. package/src/dataform-controls/toggle.tsx +41 -24
  266. package/src/dataform-controls/url.tsx +9 -1
  267. package/src/dataform-controls/utils/validated-input.tsx +50 -50
  268. package/src/dataform-controls/utils/validated-number.tsx +209 -0
  269. package/src/dataforms-layouts/panel/dropdown.tsx +12 -23
  270. package/src/dataforms-layouts/panel/index.tsx +39 -16
  271. package/src/dataforms-layouts/panel/modal.tsx +24 -30
  272. package/src/dataforms-layouts/panel/summary-button.tsx +92 -0
  273. package/src/dataviews-layouts/picker-grid/index.tsx +15 -8
  274. package/src/field-types/array.tsx +0 -8
  275. package/src/field-types/index.tsx +5 -0
  276. package/src/field-types/number.tsx +104 -0
  277. package/src/field-types/stories/index.story.tsx +170 -16
  278. package/src/index.ts +1 -0
  279. package/src/normalize-fields.ts +18 -0
  280. package/src/test/dataform.tsx +36 -0
  281. package/src/test/filter-and-sort-data-view.js +182 -138
  282. package/src/test/normalize-fields.ts +136 -0
  283. package/src/test/validation.ts +235 -0
  284. package/src/types.ts +76 -4
  285. package/src/validation.ts +32 -0
  286. package/tsconfig.tsbuildinfo +1 -1
@@ -1,13 +1,22 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import deepMerge from 'deepmerge';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
4
- import { FormTokenField } from '@wordpress/components';
5
- import { useCallback, useMemo } from '@wordpress/element';
9
+ import { privateApis } from '@wordpress/components';
10
+ import { useCallback, useMemo, useState } from '@wordpress/element';
11
+ import { _n, sprintf } from '@wordpress/i18n';
6
12
 
7
13
  /**
8
14
  * Internal dependencies
9
15
  */
10
16
  import type { DataFormControlProps } from '../types';
17
+ import { unlock } from '../lock-unlock';
18
+
19
+ const { ValidatedFormTokenField } = unlock( privateApis );
11
20
 
12
21
  export default function ArrayControl< Item >( {
13
22
  data,
@@ -15,71 +24,157 @@ export default function ArrayControl< Item >( {
15
24
  onChange,
16
25
  hideLabelFromVision,
17
26
  }: DataFormControlProps< Item > ) {
18
- const { id, label, placeholder, elements } = field;
19
- const value = field.getValue( { item: data } );
20
-
21
- const findElementByValue = useCallback(
22
- ( suggestionValue: string ) => {
23
- return elements?.find(
24
- ( suggestion ) => suggestion.value === suggestionValue
25
- );
26
- },
27
- [ elements ]
28
- );
27
+ const { label, placeholder, elements, getValue, setValue } = field;
28
+ const value = getValue( { item: data } );
29
29
 
30
- const findElementByLabel = useCallback(
31
- ( suggestionLabel: string ) => {
32
- return elements?.find(
33
- ( suggestion ) => suggestion.label === suggestionLabel
34
- );
35
- },
36
- [ elements ]
37
- );
30
+ const [ customValidity, setCustomValidity ] = useState<
31
+ | {
32
+ type: 'validating' | 'valid' | 'invalid';
33
+ message: string;
34
+ }
35
+ | undefined
36
+ >( undefined );
38
37
 
39
- // Ensure value is an array
40
- const arrayValue = useMemo(
38
+ // Convert stored values to element objects for the token field
39
+ const arrayValueAsElements = useMemo(
41
40
  () =>
42
41
  Array.isArray( value )
43
42
  ? value.map( ( token ) => {
44
- const tokenLabel = findElementByValue( token )?.label;
45
- return tokenLabel || token;
43
+ const element = elements?.find(
44
+ ( suggestion ) => suggestion.value === token
45
+ );
46
+ return element || { value: token, label: token };
46
47
  } )
47
48
  : [],
48
- [ value, findElementByValue ]
49
+ [ value, elements ]
49
50
  );
50
51
 
51
- const onChangeControl = useCallback(
52
- ( tokens: ( string | { value: string } )[] ) => {
53
- // Convert TokenItem objects to strings
54
- const stringTokens = tokens.map( ( token ) => {
55
- if ( typeof token !== 'string' ) {
52
+ const validateTokens = useCallback(
53
+ ( tokens: ( string | { value: string; label?: string } )[] ) => {
54
+ // Extract actual values from tokens for validation
55
+ const tokenValues = tokens.map( ( token ) => {
56
+ if ( typeof token === 'object' && 'value' in token ) {
56
57
  return token.value;
57
58
  }
59
+ return token;
60
+ } );
58
61
 
59
- const tokenByLabel = findElementByLabel( token );
62
+ // First, check if elements validation is required and any tokens are invalid
63
+ if ( field.isValid?.elements && elements ) {
64
+ const invalidTokens = tokenValues.filter( ( tokenValue ) => {
65
+ return ! elements.some(
66
+ ( element ) => element.value === tokenValue
67
+ );
68
+ } );
60
69
 
61
- return tokenByLabel?.value || token;
62
- } );
70
+ if ( invalidTokens.length > 0 ) {
71
+ setCustomValidity( {
72
+ type: 'invalid',
73
+ message: sprintf(
74
+ /* translators: %s: list of invalid tokens */
75
+ _n(
76
+ 'Please select from the available options: %s is invalid.',
77
+ 'Please select from the available options: %s are invalid.',
78
+ invalidTokens.length
79
+ ),
80
+ invalidTokens.join( ', ' )
81
+ ),
82
+ } );
83
+ return;
84
+ }
85
+ }
86
+
87
+ // Then check custom validation if provided.
88
+ if ( field.isValid?.custom ) {
89
+ const result = field.isValid?.custom?.(
90
+ deepMerge(
91
+ data,
92
+ setValue( {
93
+ item: data,
94
+ value: tokenValues,
95
+ } ) as Partial< Item >
96
+ ),
97
+ field
98
+ );
99
+
100
+ if ( result ) {
101
+ setCustomValidity( {
102
+ type: 'invalid',
103
+ message: result,
104
+ } );
105
+ return;
106
+ }
107
+ }
108
+
109
+ // If no validation errors, clear custom validity
110
+ setCustomValidity( undefined );
111
+ },
112
+ [ elements, data, field, setValue ]
113
+ );
63
114
 
64
- onChange( {
65
- [ id ]: stringTokens,
115
+ const onChangeControl = useCallback(
116
+ ( tokens: ( string | { value: string; label?: string } )[] ) => {
117
+ const valueTokens = tokens.map( ( token ) => {
118
+ if ( typeof token === 'object' && 'value' in token ) {
119
+ return token.value;
120
+ }
121
+ // If it's a string, it's either a new suggestion value or user input
122
+ return token;
66
123
  } );
124
+
125
+ onChange( setValue( { item: data, value: valueTokens } ) );
67
126
  },
68
- [ id, onChange, findElementByLabel ]
127
+ [ onChange, setValue, data ]
69
128
  );
70
129
 
71
130
  return (
72
- <FormTokenField
131
+ <ValidatedFormTokenField
132
+ required={ !! field.isValid?.required }
133
+ onValidate={ validateTokens }
134
+ customValidity={ customValidity }
73
135
  label={ hideLabelFromVision ? undefined : label }
74
- value={ arrayValue }
136
+ value={ arrayValueAsElements }
75
137
  onChange={ onChangeControl }
76
138
  placeholder={ placeholder }
77
- suggestions={
78
- elements?.map( ( suggestion ) => suggestion.label ) ?? []
79
- }
139
+ suggestions={ elements?.map( ( element ) => element.value ) }
140
+ __experimentalValidateInput={ ( token: string ) => {
141
+ // If elements validation is required, check if token is valid
142
+ if ( field.isValid?.elements && elements ) {
143
+ return elements.some(
144
+ ( element ) =>
145
+ element.value === token || element.label === token
146
+ );
147
+ }
148
+
149
+ // For non-elements validation, allow all tokens
150
+ return true;
151
+ } }
80
152
  __experimentalExpandOnFocus={ elements && elements.length > 0 }
81
- __next40pxDefaultSize
82
- __nextHasNoMarginBottom
153
+ __experimentalShowHowTo={ ! field.isValid?.elements }
154
+ displayTransform={ ( token: any ) => {
155
+ // For existing tokens (element objects), display their label
156
+ if ( typeof token === 'object' && 'label' in token ) {
157
+ return token.label;
158
+ }
159
+ // For suggestions (value strings), find the corresponding element and show its label
160
+ if ( typeof token === 'string' && elements ) {
161
+ const element = elements.find(
162
+ ( el ) => el.value === token
163
+ );
164
+ return element?.label || token;
165
+ }
166
+ return token;
167
+ } }
168
+ __experimentalRenderItem={ ( { item }: { item: any } ) => {
169
+ // Custom rendering for suggestion items (item is a value string)
170
+ if ( typeof item === 'string' && elements ) {
171
+ const element = elements.find(
172
+ ( el ) => el.value === item
173
+ );
174
+ return <span>{ element?.label || item }</span>;
175
+ }
176
+ return <span>{ item }</span>;
177
+ } }
83
178
  />
84
179
  );
85
180
  }
@@ -1,8 +1,13 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import deepMerge from 'deepmerge';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
4
9
  import { privateApis } from '@wordpress/components';
5
- import { useState } from '@wordpress/element';
10
+ import { useState, useCallback } from '@wordpress/element';
6
11
 
7
12
  /**
8
13
  * Internal dependencies
@@ -18,7 +23,7 @@ export default function Checkbox< Item >( {
18
23
  data,
19
24
  hideLabelFromVision,
20
25
  }: DataFormControlProps< Item > ) {
21
- const { id, getValue, label, description } = field;
26
+ const { getValue, setValue, label, description } = field;
22
27
  const [ customValidity, setCustomValidity ] =
23
28
  useState<
24
29
  React.ComponentProps<
@@ -26,36 +31,48 @@ export default function Checkbox< Item >( {
26
31
  >[ 'customValidity' ]
27
32
  >( undefined );
28
33
 
34
+ const onChangeControl = useCallback( () => {
35
+ onChange(
36
+ setValue( { item: data, value: ! getValue( { item: data } ) } )
37
+ );
38
+ }, [ data, getValue, onChange, setValue ] );
39
+
40
+ const onValidateControl = useCallback(
41
+ ( newValue: any ) => {
42
+ const message = field.isValid?.custom?.(
43
+ deepMerge(
44
+ data,
45
+ setValue( {
46
+ item: data,
47
+ value: newValue,
48
+ } ) as Partial< Item >
49
+ ),
50
+ field
51
+ );
52
+
53
+ if ( message ) {
54
+ setCustomValidity( {
55
+ type: 'invalid',
56
+ message,
57
+ } );
58
+ return;
59
+ }
60
+
61
+ setCustomValidity( undefined );
62
+ },
63
+ [ data, field, setValue ]
64
+ );
65
+
29
66
  return (
30
67
  <ValidatedCheckboxControl
31
68
  required={ !! field.isValid?.required }
32
- onValidate={ ( newValue: any ) => {
33
- const message = field.isValid?.custom?.(
34
- {
35
- ...data,
36
- [ id ]: newValue,
37
- },
38
- field
39
- );
40
-
41
- if ( message ) {
42
- setCustomValidity( {
43
- type: 'invalid',
44
- message,
45
- } );
46
- return;
47
- }
48
-
49
- setCustomValidity( undefined );
50
- } }
69
+ onValidate={ onValidateControl }
51
70
  customValidity={ customValidity }
52
71
  hidden={ hideLabelFromVision }
53
72
  label={ label }
54
73
  help={ description }
55
74
  checked={ getValue( { item: data } ) }
56
- onChange={ () =>
57
- onChange( { [ id ]: ! getValue( { item: data } ) } )
58
- }
75
+ onChange={ onChangeControl }
59
76
  />
60
77
  );
61
78
  }
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { colord } from 'colord';
5
+ import deepMerge from 'deepmerge';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -75,7 +76,7 @@ export default function Color< Item >( {
75
76
  onChange,
76
77
  hideLabelFromVision,
77
78
  }: DataFormControlProps< Item > ) {
78
- const { id, label, placeholder, description } = field;
79
+ const { label, placeholder, description, setValue } = field;
79
80
  const value = field.getValue( { item: data } ) || '';
80
81
  const [ customValidity, setCustomValidity ] =
81
82
  useState<
@@ -86,40 +87,48 @@ export default function Color< Item >( {
86
87
 
87
88
  const handleColorChange = useCallback(
88
89
  ( colorObject: any ) => {
89
- onChange( { [ id ]: colorObject.toHex() } );
90
+ onChange( setValue( { item: data, value: colorObject.toHex() } ) );
90
91
  },
91
- [ id, onChange ]
92
+ [ data, onChange, setValue ]
92
93
  );
93
94
 
94
95
  const handleInputChange = useCallback(
95
96
  ( newValue: string | undefined ) => {
96
- onChange( { [ id ]: newValue || '' } );
97
+ onChange( setValue( { item: data, value: newValue || '' } ) );
97
98
  },
98
- [ id, onChange ]
99
+ [ data, onChange, setValue ]
100
+ );
101
+
102
+ const onValidateControl = useCallback(
103
+ ( newValue: any ) => {
104
+ const message = field.isValid?.custom?.(
105
+ deepMerge(
106
+ data,
107
+ setValue( {
108
+ item: data,
109
+ value: newValue,
110
+ } ) as Partial< Item >
111
+ ),
112
+ field
113
+ );
114
+
115
+ if ( message ) {
116
+ setCustomValidity( {
117
+ type: 'invalid',
118
+ message,
119
+ } );
120
+ return;
121
+ }
122
+
123
+ setCustomValidity( undefined );
124
+ },
125
+ [ data, field, setValue ]
99
126
  );
100
127
 
101
128
  return (
102
129
  <ValidatedInputControl
103
130
  required={ !! field.isValid?.required }
104
- onValidate={ ( newValue: any ) => {
105
- const message = field.isValid?.custom?.(
106
- {
107
- ...data,
108
- [ id ]: newValue,
109
- },
110
- field
111
- );
112
-
113
- if ( message ) {
114
- setCustomValidity( {
115
- type: 'invalid',
116
- message,
117
- } );
118
- return;
119
- }
120
-
121
- setCustomValidity( undefined );
122
- } }
131
+ onValidate={ onValidateControl }
123
132
  customValidity={ customValidity }
124
133
  label={ label }
125
134
  placeholder={ placeholder }
@@ -31,6 +31,7 @@ import {
31
31
  */
32
32
  import RelativeDateControl, {
33
33
  TIME_UNITS_OPTIONS,
34
+ type DateRelative,
34
35
  } from './relative-date-control';
35
36
  import {
36
37
  OPERATOR_IN_THE_PAST,
@@ -42,6 +43,8 @@ import type { DataFormControlProps } from '../types';
42
43
 
43
44
  const { DateCalendar, DateRangeCalendar } = unlock( componentsPrivateApis );
44
45
 
46
+ type DateRange = [ string, string ] | undefined;
47
+
45
48
  const DATE_PRESETS: {
46
49
  id: string;
47
50
  label: string;
@@ -146,7 +149,7 @@ function CalendarDateControl( {
146
149
  }: {
147
150
  id: string;
148
151
  value: string | undefined;
149
- onChange: ( value: any ) => void;
152
+ onChange: ( value: string | undefined ) => void;
150
153
  label: string;
151
154
  hideLabelFromVision?: boolean;
152
155
  className?: string;
@@ -165,10 +168,10 @@ function CalendarDateControl( {
165
168
  const dateValue = newDate
166
169
  ? format( newDate, 'yyyy-MM-dd' )
167
170
  : undefined;
168
- onChange( { [ id ]: dateValue } );
171
+ onChange( dateValue );
169
172
  setSelectedPresetId( null );
170
173
  },
171
- [ id, onChange ]
174
+ [ onChange ]
172
175
  );
173
176
 
174
177
  const handlePresetClick = useCallback(
@@ -177,15 +180,15 @@ function CalendarDateControl( {
177
180
  const dateValue = formatDate( presetDate );
178
181
 
179
182
  setCalendarMonth( presetDate );
180
- onChange( { [ id ]: dateValue } );
183
+ onChange( dateValue );
181
184
  setSelectedPresetId( preset.id );
182
185
  },
183
- [ id, onChange ]
186
+ [ onChange ]
184
187
  );
185
188
 
186
189
  const handleManualDateChange = useCallback(
187
190
  ( newValue?: string ) => {
188
- onChange( { [ id ]: newValue } );
191
+ onChange( newValue );
189
192
  if ( newValue ) {
190
193
  const parsedDate = parseDate( newValue );
191
194
  if ( parsedDate ) {
@@ -194,7 +197,7 @@ function CalendarDateControl( {
194
197
  }
195
198
  setSelectedPresetId( null );
196
199
  },
197
- [ id, onChange ]
200
+ [ onChange ]
198
201
  );
199
202
 
200
203
  const {
@@ -276,8 +279,8 @@ function CalendarDateRangeControl( {
276
279
  className,
277
280
  }: {
278
281
  id: string;
279
- value: [ string, string ] | undefined;
280
- onChange: ( value: any ) => void;
282
+ value: DateRange;
283
+ onChange: ( value: DateRange ) => void;
281
284
  label: string;
282
285
  hideLabelFromVision?: boolean;
283
286
  className?: string;
@@ -305,15 +308,13 @@ function CalendarDateRangeControl( {
305
308
  const updateDateRange = useCallback(
306
309
  ( fromDate?: Date | string, toDate?: Date | string ) => {
307
310
  if ( fromDate && toDate ) {
308
- onChange( {
309
- [ id ]: [ formatDate( fromDate ), formatDate( toDate ) ],
310
- } );
311
+ onChange( [ formatDate( fromDate ), formatDate( toDate ) ] );
311
312
  } else if ( ! fromDate && ! toDate ) {
312
- onChange( { [ id ]: undefined } );
313
+ onChange( undefined );
313
314
  }
314
315
  // Do nothing if only one date is set - wait for both
315
316
  },
316
- [ id, onChange ]
317
+ [ onChange ]
317
318
  );
318
319
 
319
320
  const onSelectCalendarRange = useCallback(
@@ -446,8 +447,33 @@ export default function DateControl< Item >( {
446
447
  hideLabelFromVision,
447
448
  operator,
448
449
  }: DataFormControlProps< Item > ) {
449
- const { id, label } = field;
450
- const value = field.getValue( { item: data } );
450
+ const { id, label, getValue, setValue } = field;
451
+ const value = getValue( { item: data } );
452
+
453
+ const onChangeRelativeDateControl = useCallback(
454
+ ( newValue: DateRelative ) => {
455
+ onChange( setValue( { item: data, value: newValue } ) );
456
+ },
457
+ [ data, onChange, setValue ]
458
+ );
459
+
460
+ const onChangeCalendarDateRangeControl = useCallback(
461
+ ( newValue: DateRange ) => {
462
+ onChange(
463
+ setValue( {
464
+ item: data,
465
+ value: newValue,
466
+ } )
467
+ );
468
+ },
469
+ [ data, onChange, setValue ]
470
+ );
471
+
472
+ const onChangeCalendarDateControl = useCallback(
473
+ ( newValue: string | undefined ) =>
474
+ onChange( setValue( { item: data, value: newValue } ) ),
475
+ [ data, onChange, setValue ]
476
+ );
451
477
 
452
478
  if ( operator === OPERATOR_IN_THE_PAST || operator === OPERATOR_OVER ) {
453
479
  return (
@@ -455,7 +481,7 @@ export default function DateControl< Item >( {
455
481
  className="dataviews-controls__date"
456
482
  id={ id }
457
483
  value={ value && typeof value === 'object' ? value : {} }
458
- onChange={ onChange }
484
+ onChange={ onChangeRelativeDateControl }
459
485
  label={ label }
460
486
  hideLabelFromVision={ hideLabelFromVision }
461
487
  options={ TIME_UNITS_OPTIONS[ operator ] }
@@ -464,14 +490,14 @@ export default function DateControl< Item >( {
464
490
  }
465
491
 
466
492
  if ( operator === OPERATOR_BETWEEN ) {
467
- let dateRangeValue: [ string, string ] | undefined;
493
+ let dateRangeValue: DateRange;
468
494
  if (
469
495
  Array.isArray( value ) &&
470
496
  value.length === 2 &&
471
497
  value.every( ( date ) => typeof date === 'string' )
472
498
  ) {
473
499
  // Ensure the value is expected format
474
- dateRangeValue = value as unknown as [ string, string ];
500
+ dateRangeValue = value as DateRange;
475
501
  }
476
502
 
477
503
  return (
@@ -479,7 +505,7 @@ export default function DateControl< Item >( {
479
505
  className="dataviews-controls__date"
480
506
  id={ id }
481
507
  value={ dateRangeValue }
482
- onChange={ onChange }
508
+ onChange={ onChangeCalendarDateRangeControl }
483
509
  label={ label }
484
510
  hideLabelFromVision={ hideLabelFromVision }
485
511
  />
@@ -491,7 +517,7 @@ export default function DateControl< Item >( {
491
517
  className="dataviews-controls__date"
492
518
  id={ id }
493
519
  value={ typeof value === 'string' ? value : undefined }
494
- onChange={ onChange }
520
+ onChange={ onChangeCalendarDateControl }
495
521
  label={ label }
496
522
  hideLabelFromVision={ hideLabelFromVision }
497
523
  />