@wordpress/dataviews 9.0.1-next.6f42e1382.0 → 9.1.1-next.f56bd8138.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 (201) hide show
  1. package/CHANGELOG.md +22 -2
  2. package/README.md +107 -11
  3. package/build/components/dataviews-filters/input-widget.js +48 -4
  4. package/build/components/dataviews-filters/input-widget.js.map +1 -1
  5. package/build/components/dataviews-view-config/index.js +22 -3
  6. package/build/components/dataviews-view-config/index.js.map +1 -1
  7. package/build/dataform-controls/array.js +117 -29
  8. package/build/dataform-controls/array.js.map +1 -1
  9. package/build/dataform-controls/checkbox.js +31 -20
  10. package/build/dataform-controls/checkbox.js.map +1 -1
  11. package/build/dataform-controls/color.js +29 -24
  12. package/build/dataform-controls/color.js.map +1 -1
  13. package/build/dataform-controls/date.js +32 -24
  14. package/build/dataform-controls/date.js.map +1 -1
  15. package/build/dataform-controls/datetime.js +133 -19
  16. package/build/dataform-controls/datetime.js.map +1 -1
  17. package/build/dataform-controls/email.js +7 -1
  18. package/build/dataform-controls/email.js.map +1 -1
  19. package/build/dataform-controls/index.js +23 -0
  20. package/build/dataform-controls/index.js.map +1 -1
  21. package/build/dataform-controls/integer.js +47 -34
  22. package/build/dataform-controls/integer.js.map +1 -1
  23. package/build/dataform-controls/radio.js +42 -9
  24. package/build/dataform-controls/radio.js.map +1 -1
  25. package/build/dataform-controls/relative-date-control.js +6 -10
  26. package/build/dataform-controls/relative-date-control.js.map +1 -1
  27. package/build/dataform-controls/select.js +41 -10
  28. package/build/dataform-controls/select.js.map +1 -1
  29. package/build/dataform-controls/telephone.js +7 -1
  30. package/build/dataform-controls/telephone.js.map +1 -1
  31. package/build/dataform-controls/text.js +14 -2
  32. package/build/dataform-controls/text.js.map +1 -1
  33. package/build/dataform-controls/textarea.js +33 -20
  34. package/build/dataform-controls/textarea.js.map +1 -1
  35. package/build/dataform-controls/toggle-group.js +36 -6
  36. package/build/dataform-controls/toggle-group.js.map +1 -1
  37. package/build/dataform-controls/toggle.js +33 -22
  38. package/build/dataform-controls/toggle.js.map +1 -1
  39. package/build/dataform-controls/url.js +7 -1
  40. package/build/dataform-controls/url.js.map +1 -1
  41. package/build/dataform-controls/utils/validated-input.js +34 -32
  42. package/build/dataform-controls/utils/validated-input.js.map +1 -1
  43. package/build/dataforms-layouts/panel/dropdown.js +10 -14
  44. package/build/dataforms-layouts/panel/dropdown.js.map +1 -1
  45. package/build/dataforms-layouts/panel/index.js +24 -11
  46. package/build/dataforms-layouts/panel/index.js.map +1 -1
  47. package/build/dataforms-layouts/panel/modal.js +22 -27
  48. package/build/dataforms-layouts/panel/modal.js.map +1 -1
  49. package/build/dataforms-layouts/panel/summary-button.js +67 -0
  50. package/build/dataforms-layouts/panel/summary-button.js.map +1 -0
  51. package/build/field-types/array.js +0 -6
  52. package/build/field-types/array.js.map +1 -1
  53. package/build/index.js +7 -0
  54. package/build/index.js.map +1 -1
  55. package/build/normalize-fields.js +17 -0
  56. package/build/normalize-fields.js.map +1 -1
  57. package/build/types.js.map +1 -1
  58. package/build/validation.js +18 -1
  59. package/build/validation.js.map +1 -1
  60. package/build-module/components/dataviews-filters/input-widget.js +48 -4
  61. package/build-module/components/dataviews-filters/input-widget.js.map +1 -1
  62. package/build-module/components/dataviews-view-config/index.js +22 -3
  63. package/build-module/components/dataviews-view-config/index.js.map +1 -1
  64. package/build-module/dataform-controls/array.js +120 -32
  65. package/build-module/dataform-controls/array.js.map +1 -1
  66. package/build-module/dataform-controls/checkbox.js +31 -21
  67. package/build-module/dataform-controls/checkbox.js.map +1 -1
  68. package/build-module/dataform-controls/color.js +28 -24
  69. package/build-module/dataform-controls/color.js.map +1 -1
  70. package/build-module/dataform-controls/date.js +32 -24
  71. package/build-module/dataform-controls/date.js.map +1 -1
  72. package/build-module/dataform-controls/datetime.js +135 -21
  73. package/build-module/dataform-controls/datetime.js.map +1 -1
  74. package/build-module/dataform-controls/email.js +7 -1
  75. package/build-module/dataform-controls/email.js.map +1 -1
  76. package/build-module/dataform-controls/index.js +23 -0
  77. package/build-module/dataform-controls/index.js.map +1 -1
  78. package/build-module/dataform-controls/integer.js +46 -34
  79. package/build-module/dataform-controls/integer.js.map +1 -1
  80. package/build-module/dataform-controls/radio.js +44 -11
  81. package/build-module/dataform-controls/radio.js.map +1 -1
  82. package/build-module/dataform-controls/relative-date-control.js +6 -10
  83. package/build-module/dataform-controls/relative-date-control.js.map +1 -1
  84. package/build-module/dataform-controls/select.js +43 -12
  85. package/build-module/dataform-controls/select.js.map +1 -1
  86. package/build-module/dataform-controls/telephone.js +7 -1
  87. package/build-module/dataform-controls/telephone.js.map +1 -1
  88. package/build-module/dataform-controls/text.js +14 -2
  89. package/build-module/dataform-controls/text.js.map +1 -1
  90. package/build-module/dataform-controls/textarea.js +32 -20
  91. package/build-module/dataform-controls/textarea.js.map +1 -1
  92. package/build-module/dataform-controls/toggle-group.js +38 -8
  93. package/build-module/dataform-controls/toggle-group.js.map +1 -1
  94. package/build-module/dataform-controls/toggle.js +33 -23
  95. package/build-module/dataform-controls/toggle.js.map +1 -1
  96. package/build-module/dataform-controls/url.js +7 -1
  97. package/build-module/dataform-controls/url.js.map +1 -1
  98. package/build-module/dataform-controls/utils/validated-input.js +34 -33
  99. package/build-module/dataform-controls/utils/validated-input.js.map +1 -1
  100. package/build-module/dataforms-layouts/panel/dropdown.js +10 -15
  101. package/build-module/dataforms-layouts/panel/dropdown.js.map +1 -1
  102. package/build-module/dataforms-layouts/panel/index.js +24 -11
  103. package/build-module/dataforms-layouts/panel/index.js.map +1 -1
  104. package/build-module/dataforms-layouts/panel/modal.js +22 -28
  105. package/build-module/dataforms-layouts/panel/modal.js.map +1 -1
  106. package/build-module/dataforms-layouts/panel/summary-button.js +60 -0
  107. package/build-module/dataforms-layouts/panel/summary-button.js.map +1 -0
  108. package/build-module/field-types/array.js +0 -6
  109. package/build-module/field-types/array.js.map +1 -1
  110. package/build-module/index.js +1 -0
  111. package/build-module/index.js.map +1 -1
  112. package/build-module/normalize-fields.js +15 -0
  113. package/build-module/normalize-fields.js.map +1 -1
  114. package/build-module/types.js.map +1 -1
  115. package/build-module/validation.js +18 -1
  116. package/build-module/validation.js.map +1 -1
  117. package/build-types/components/dataform/stories/index.story.d.ts +3 -0
  118. package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
  119. package/build-types/components/dataviews/stories/fixtures.d.ts +4 -2
  120. package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
  121. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  122. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  123. package/build-types/dataform-controls/array.d.ts.map +1 -1
  124. package/build-types/dataform-controls/checkbox.d.ts.map +1 -1
  125. package/build-types/dataform-controls/color.d.ts.map +1 -1
  126. package/build-types/dataform-controls/date.d.ts.map +1 -1
  127. package/build-types/dataform-controls/datetime.d.ts.map +1 -1
  128. package/build-types/dataform-controls/email.d.ts.map +1 -1
  129. package/build-types/dataform-controls/index.d.ts +1 -1
  130. package/build-types/dataform-controls/index.d.ts.map +1 -1
  131. package/build-types/dataform-controls/integer.d.ts.map +1 -1
  132. package/build-types/dataform-controls/radio.d.ts.map +1 -1
  133. package/build-types/dataform-controls/relative-date-control.d.ts +6 -5
  134. package/build-types/dataform-controls/relative-date-control.d.ts.map +1 -1
  135. package/build-types/dataform-controls/select.d.ts.map +1 -1
  136. package/build-types/dataform-controls/telephone.d.ts.map +1 -1
  137. package/build-types/dataform-controls/text.d.ts +1 -1
  138. package/build-types/dataform-controls/text.d.ts.map +1 -1
  139. package/build-types/dataform-controls/textarea.d.ts +1 -1
  140. package/build-types/dataform-controls/textarea.d.ts.map +1 -1
  141. package/build-types/dataform-controls/toggle-group.d.ts.map +1 -1
  142. package/build-types/dataform-controls/toggle.d.ts.map +1 -1
  143. package/build-types/dataform-controls/url.d.ts.map +1 -1
  144. package/build-types/dataform-controls/utils/validated-input.d.ts +4 -4
  145. package/build-types/dataform-controls/utils/validated-input.d.ts.map +1 -1
  146. package/build-types/dataforms-layouts/panel/dropdown.d.ts +2 -1
  147. package/build-types/dataforms-layouts/panel/dropdown.d.ts.map +1 -1
  148. package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
  149. package/build-types/dataforms-layouts/panel/modal.d.ts +2 -1
  150. package/build-types/dataforms-layouts/panel/modal.d.ts.map +1 -1
  151. package/build-types/dataforms-layouts/panel/summary-button.d.ts +15 -0
  152. package/build-types/dataforms-layouts/panel/summary-button.d.ts.map +1 -0
  153. package/build-types/field-types/array.d.ts.map +1 -1
  154. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  155. package/build-types/index.d.ts +1 -0
  156. package/build-types/index.d.ts.map +1 -1
  157. package/build-types/normalize-fields.d.ts +3 -0
  158. package/build-types/normalize-fields.d.ts.map +1 -1
  159. package/build-types/types.d.ts +68 -4
  160. package/build-types/types.d.ts.map +1 -1
  161. package/build-types/validation.d.ts.map +1 -1
  162. package/build-wp/index.js +2009 -1489
  163. package/package.json +16 -15
  164. package/src/components/dataform/stories/index.story.tsx +509 -8
  165. package/src/components/dataviews/stories/fixtures.tsx +99 -41
  166. package/src/components/dataviews/stories/index.story.tsx +1 -1
  167. package/src/components/dataviews-filters/input-widget.tsx +44 -5
  168. package/src/components/dataviews-picker/stories/index.story.tsx +1 -1
  169. package/src/components/dataviews-view-config/index.tsx +18 -3
  170. package/src/dataform-controls/array.tsx +139 -44
  171. package/src/dataform-controls/checkbox.tsx +41 -24
  172. package/src/dataform-controls/color.tsx +33 -24
  173. package/src/dataform-controls/date.tsx +47 -21
  174. package/src/dataform-controls/datetime.tsx +171 -23
  175. package/src/dataform-controls/email.tsx +9 -1
  176. package/src/dataform-controls/index.tsx +26 -0
  177. package/src/dataform-controls/integer.tsx +82 -49
  178. package/src/dataform-controls/radio.tsx +53 -11
  179. package/src/dataform-controls/relative-date-control.tsx +11 -10
  180. package/src/dataform-controls/select.tsx +53 -10
  181. package/src/dataform-controls/telephone.tsx +9 -1
  182. package/src/dataform-controls/text.tsx +18 -1
  183. package/src/dataform-controls/textarea.tsx +38 -24
  184. package/src/dataform-controls/toggle-group.tsx +50 -10
  185. package/src/dataform-controls/toggle.tsx +41 -24
  186. package/src/dataform-controls/url.tsx +9 -1
  187. package/src/dataform-controls/utils/validated-input.tsx +50 -50
  188. package/src/dataforms-layouts/panel/dropdown.tsx +12 -23
  189. package/src/dataforms-layouts/panel/index.tsx +39 -16
  190. package/src/dataforms-layouts/panel/modal.tsx +24 -30
  191. package/src/dataforms-layouts/panel/summary-button.tsx +92 -0
  192. package/src/field-types/array.tsx +0 -8
  193. package/src/field-types/stories/index.story.tsx +89 -1
  194. package/src/index.ts +1 -0
  195. package/src/normalize-fields.ts +18 -0
  196. package/src/test/filter-and-sort-data-view.js +148 -138
  197. package/src/test/normalize-fields.ts +114 -0
  198. package/src/test/validation.ts +192 -0
  199. package/src/types.ts +75 -4
  200. package/src/validation.ts +30 -0
  201. package/tsconfig.tsbuildinfo +1 -1
@@ -25,8 +25,10 @@ export type Theme = {
25
25
 
26
26
  export type SpaceObject = {
27
27
  id: number;
28
- title: string;
29
- description: string;
28
+ name: {
29
+ title: string;
30
+ description: string;
31
+ };
30
32
  image: string;
31
33
  type: string;
32
34
  isPlanet: boolean;
@@ -40,9 +42,11 @@ export type SpaceObject = {
40
42
  export const data: SpaceObject[] = [
41
43
  {
42
44
  id: 1,
43
- title: 'Moon',
44
- description:
45
- 'The Moon is Earth’s only natural satellite, orbiting at an average distance of 384,400 kilometers with a synchronous rotation that leads to fixed lunar phases as seen from Earth. Its cratered surface and subtle glow define night skies, inspiring exploration missions and influencing tides and biological rhythms worldwide.',
45
+ name: {
46
+ title: 'Moon',
47
+ description:
48
+ "The Moon is Earth's only natural satellite, orbiting at an average distance of 384,400 kilometers with a synchronous rotation that leads to fixed lunar phases as seen from Earth. Its cratered surface and subtle glow define night skies, inspiring exploration missions and influencing tides and biological rhythms worldwide.",
49
+ },
46
50
  image: 'https://live.staticflickr.com/7398/9458193857_e1256123e3_z.jpg',
47
51
  type: 'Satellite',
48
52
  isPlanet: false,
@@ -54,8 +58,10 @@ export const data: SpaceObject[] = [
54
58
  },
55
59
  {
56
60
  id: 2,
57
- title: 'Io',
58
- description: 'Moon of Jupiter',
61
+ name: {
62
+ title: 'Io',
63
+ description: 'Moon of Jupiter',
64
+ },
59
65
  image: 'https://live.staticflickr.com/5482/9460973502_07e8ab81fe_z.jpg',
60
66
  type: 'Satellite',
61
67
  isPlanet: false,
@@ -67,8 +73,10 @@ export const data: SpaceObject[] = [
67
73
  },
68
74
  {
69
75
  id: 3,
70
- title: 'Europa',
71
- description: 'Moon of Jupiter',
76
+ name: {
77
+ title: 'Europa',
78
+ description: 'Moon of Jupiter',
79
+ },
72
80
  image: 'https://live.staticflickr.com/65535/31499273012_baf5f38cc1_z.jpg',
73
81
  type: 'Satellite',
74
82
  isPlanet: false,
@@ -80,8 +88,10 @@ export const data: SpaceObject[] = [
80
88
  },
81
89
  {
82
90
  id: 4,
83
- title: 'Ganymede',
84
- description: 'Largest moon of Jupiter',
91
+ name: {
92
+ title: 'Ganymede',
93
+ description: 'Largest moon of Jupiter',
94
+ },
85
95
  image: 'https://live.staticflickr.com/7816/33436473218_a836235935_k.jpg',
86
96
  type: 'Satellite',
87
97
  isPlanet: false,
@@ -93,8 +103,10 @@ export const data: SpaceObject[] = [
93
103
  },
94
104
  {
95
105
  id: 5,
96
- title: 'Callisto',
97
- description: 'Outermost Galilean moon of Jupiter',
106
+ name: {
107
+ title: 'Callisto',
108
+ description: 'Outermost Galilean moon of Jupiter',
109
+ },
98
110
  image: 'https://live.staticflickr.com/804/27604150528_4512448a9c_c.jpg',
99
111
  type: 'Satellite',
100
112
  isPlanet: false,
@@ -106,8 +118,10 @@ export const data: SpaceObject[] = [
106
118
  },
107
119
  {
108
120
  id: 6,
109
- title: 'Amalthea',
110
- description: 'Small irregular moon of Jupiter',
121
+ name: {
122
+ title: 'Amalthea',
123
+ description: 'Small irregular moon of Jupiter',
124
+ },
111
125
  image: 'https://upload.wikimedia.org/wikipedia/commons/6/62/Amalthea.gif',
112
126
  type: 'Satellite',
113
127
  isPlanet: false,
@@ -119,8 +133,10 @@ export const data: SpaceObject[] = [
119
133
  },
120
134
  {
121
135
  id: 7,
122
- title: 'Himalia',
123
- description: 'Largest irregular moon of Jupiter',
136
+ name: {
137
+ title: 'Himalia',
138
+ description: 'Largest irregular moon of Jupiter',
139
+ },
124
140
  image: 'https://upload.wikimedia.org/wikipedia/commons/c/c2/Cassini-Huygens_Image_of_Himalia.png',
125
141
  type: 'Satellite',
126
142
  isPlanet: false,
@@ -132,8 +148,10 @@ export const data: SpaceObject[] = [
132
148
  },
133
149
  {
134
150
  id: 8,
135
- title: 'Neptune',
136
- description: 'Ice giant in the Solar system',
151
+ name: {
152
+ title: 'Neptune',
153
+ description: 'Ice giant in the Solar system',
154
+ },
137
155
  image: 'https://live.staticflickr.com/65535/29523683990_000ff4720c_z.jpg',
138
156
  type: 'Ice giant',
139
157
  isPlanet: true,
@@ -145,8 +163,10 @@ export const data: SpaceObject[] = [
145
163
  },
146
164
  {
147
165
  id: 9,
148
- title: 'Triton',
149
- description: 'Largest moon of Neptune',
166
+ name: {
167
+ title: 'Triton',
168
+ description: 'Largest moon of Neptune',
169
+ },
150
170
  image: 'https://live.staticflickr.com/65535/50728384241_02c5126c30_h.jpg',
151
171
  type: 'Satellite',
152
172
  isPlanet: false,
@@ -158,8 +178,10 @@ export const data: SpaceObject[] = [
158
178
  },
159
179
  {
160
180
  id: 10,
161
- title: 'Nereid',
162
- description: 'Irregular moon of Neptune',
181
+ name: {
182
+ title: 'Nereid',
183
+ description: 'Irregular moon of Neptune',
184
+ },
163
185
  image: 'https://upload.wikimedia.org/wikipedia/commons/b/b0/Nereid-Voyager2.jpg',
164
186
  type: 'Satellite',
165
187
  isPlanet: false,
@@ -171,8 +193,10 @@ export const data: SpaceObject[] = [
171
193
  },
172
194
  {
173
195
  id: 11,
174
- title: 'Proteus',
175
- description: 'Second-largest moon of Neptune',
196
+ name: {
197
+ title: 'Proteus',
198
+ description: 'Second-largest moon of Neptune',
199
+ },
176
200
  image: 'https://live.staticflickr.com/65535/50727825808_bf427e007b_c.jpg',
177
201
  type: 'Satellite',
178
202
  isPlanet: false,
@@ -184,8 +208,10 @@ export const data: SpaceObject[] = [
184
208
  },
185
209
  {
186
210
  id: 12,
187
- title: 'Mercury',
188
- description: 'Terrestrial planet in the Solar system',
211
+ name: {
212
+ title: 'Mercury',
213
+ description: 'Terrestrial planet in the Solar system',
214
+ },
189
215
  image: 'https://live.staticflickr.com/813/40199101735_e5e92ffd11_z.jpg',
190
216
  type: 'Terrestrial',
191
217
  isPlanet: true,
@@ -197,8 +223,10 @@ export const data: SpaceObject[] = [
197
223
  },
198
224
  {
199
225
  id: 13,
200
- title: 'Venus',
201
- description: 'La planète Vénus',
226
+ name: {
227
+ title: 'Venus',
228
+ description: 'La planète Vénus',
229
+ },
202
230
  image: 'https://live.staticflickr.com/8025/7544560662_900e717727_z.jpg',
203
231
  type: 'Terrestrial',
204
232
  isPlanet: true,
@@ -210,8 +238,10 @@ export const data: SpaceObject[] = [
210
238
  },
211
239
  {
212
240
  id: 14,
213
- title: 'Earth',
214
- description: 'Terrestrial planet in the Solar system',
241
+ name: {
242
+ title: 'Earth',
243
+ description: 'Terrestrial planet in the Solar system',
244
+ },
215
245
  image: 'https://live.staticflickr.com/3762/9460163562_964fe6af07_z.jpg',
216
246
  type: 'Terrestrial',
217
247
  isPlanet: true,
@@ -223,8 +253,10 @@ export const data: SpaceObject[] = [
223
253
  },
224
254
  {
225
255
  id: 15,
226
- title: 'Mars',
227
- description: 'Terrestrial planet in the Solar system',
256
+ name: {
257
+ title: 'Mars',
258
+ description: 'Terrestrial planet in the Solar system',
259
+ },
228
260
  image: 'https://live.staticflickr.com/8151/7651156426_e047f4d219_z.jpg',
229
261
  type: 'Terrestrial',
230
262
  isPlanet: true,
@@ -236,8 +268,10 @@ export const data: SpaceObject[] = [
236
268
  },
237
269
  {
238
270
  id: 16,
239
- title: 'Jupiter',
240
- description: 'Gas giant in the Solar system',
271
+ name: {
272
+ title: 'Jupiter',
273
+ description: 'Gas giant in the Solar system',
274
+ },
241
275
  image: 'https://staging-jubilee.flickr.com/2853/9458010071_6e6fc41408_z.jpg',
242
276
  type: 'Gas giant',
243
277
  isPlanet: true,
@@ -249,8 +283,10 @@ export const data: SpaceObject[] = [
249
283
  },
250
284
  {
251
285
  id: 17,
252
- title: 'Saturn',
253
- description: 'Gas giant in the Solar system',
286
+ name: {
287
+ title: 'Saturn',
288
+ description: 'Gas giant in the Solar system',
289
+ },
254
290
  image: 'https://live.staticflickr.com/5524/9464658509_fc2d83dff5_z.jpg',
255
291
  type: 'Gas giant',
256
292
  isPlanet: true,
@@ -262,8 +298,10 @@ export const data: SpaceObject[] = [
262
298
  },
263
299
  {
264
300
  id: 18,
265
- title: 'Uranus',
266
- description: 'Ice giant in the Solar system',
301
+ name: {
302
+ title: 'Uranus',
303
+ description: 'Ice giant in the Solar system',
304
+ },
267
305
  image: 'https://live.staticflickr.com/65535/5553350875_3072df91e2_c.jpg',
268
306
  type: 'Ice giant',
269
307
  isPlanet: true,
@@ -722,7 +760,7 @@ export const actions: Action< SpaceObject >[] = [
722
760
  return (
723
761
  <VStack spacing="5">
724
762
  <Text>
725
- { `Are you sure you want to delete "${ items[ 0 ].title }"?` }
763
+ { `Are you sure you want to delete "${ items[ 0 ].name.title }"?` }
726
764
  </Text>
727
765
  <HStack justify="right">
728
766
  <Button
@@ -777,6 +815,15 @@ export const fields: Field< SpaceObject >[] = [
777
815
  filterBy: {
778
816
  operators: [ 'contains', 'notContains', 'startsWith' ],
779
817
  },
818
+ isValid: {
819
+ required: true,
820
+ },
821
+ getValue: ( { item } ) => item.name.title,
822
+ setValue: ( { value } ) => ( {
823
+ name: {
824
+ title: value,
825
+ },
826
+ } ),
780
827
  },
781
828
  {
782
829
  id: 'date',
@@ -806,6 +853,9 @@ export const fields: Field< SpaceObject >[] = [
806
853
  id: 'isPlanet',
807
854
  label: 'Is Planet',
808
855
  type: 'boolean',
856
+ setValue: ( { value } ) => ( {
857
+ isPlanet: value === 'true',
858
+ } ),
809
859
  elements: [
810
860
  { value: true, label: 'True' },
811
861
  { value: false, label: 'False' },
@@ -823,7 +873,15 @@ export const fields: Field< SpaceObject >[] = [
823
873
  type: 'text',
824
874
  enableSorting: false,
825
875
  enableGlobalSearch: true,
826
- filterBy: false,
876
+ filterBy: {
877
+ operators: [ 'contains', 'notContains', 'startsWith' ],
878
+ },
879
+ getValue: ( { item } ) => item.name.description,
880
+ setValue: ( { value } ) => ( {
881
+ name: {
882
+ description: value,
883
+ },
884
+ } ),
827
885
  },
828
886
  {
829
887
  label: 'Email',
@@ -96,7 +96,7 @@ export const Default = ( { perPageSizes = [ 10, 25, 50, 100 ] } ) => {
96
96
  onClick={ ( e ) => {
97
97
  e.stopPropagation();
98
98
  // eslint-disable-next-line no-alert
99
- alert( 'Clicked: ' + item.title );
99
+ alert( 'Clicked: ' + item.name.title );
100
100
  } }
101
101
  { ...props }
102
102
  />
@@ -32,13 +32,52 @@ export default function InputWidget( {
32
32
  const currentFilter = view.filters?.find(
33
33
  ( f ) => f.field === filter.field
34
34
  );
35
-
36
- const field = fields.find( ( f ) => f.id === filter.field );
37
35
  const currentValue = getCurrentValue( filter, currentFilter );
36
+
37
+ /*
38
+ * We are reusing the field.Edit component for filters. By doing so,
39
+ * we get for free a filter control specific to the field type
40
+ * and other aspects of the field API (Edit control configuration, etc.).
41
+ *
42
+ * This approach comes with an issue: the field.Edit controls work with getValue
43
+ * and setValue methods, which take an item (Item) as parameter. But, at this point,
44
+ * we don't have an item and we don't know how to create one, either.
45
+ *
46
+ * So, what we do is to prepare the data and the relevant field configuration
47
+ * as if Item was a plain object whose keys are the field ids:
48
+ *
49
+ * {
50
+ * [ fieldOne.id ]: value,
51
+ * [ fieldTwo.id ]: value,
52
+ * }
53
+ *
54
+ */
55
+ const field = useMemo( () => {
56
+ const currentField = fields.find( ( f ) => f.id === filter.field );
57
+ if ( currentField ) {
58
+ return {
59
+ ...currentField,
60
+ // Deactivate validation for filters.
61
+ isValid: {
62
+ required: false,
63
+ custom: () => null,
64
+ },
65
+ // Configure getValue/setValue as if Item was a plain object.
66
+ getValue: ( { item }: { item: any } ) =>
67
+ item[ currentField.id ],
68
+ setValue: ( { value }: { value: any } ) => ( {
69
+ [ currentField.id ]: value,
70
+ } ),
71
+ };
72
+ }
73
+ return currentField;
74
+ }, [ fields, filter.field ] );
75
+
38
76
  const data = useMemo( () => {
39
77
  return ( view.filters ?? [] ).reduce(
40
- ( acc, f ) => {
41
- acc[ f.field ] = f.value;
78
+ ( acc, activeFilter ) => {
79
+ // We can now assume the field is stored as a Item prop.
80
+ acc[ activeFilter.field ] = activeFilter.value;
42
81
  return acc;
43
82
  },
44
83
  {} as Record< string, any >
@@ -49,7 +88,7 @@ export default function InputWidget( {
49
88
  if ( ! field || ! currentFilter ) {
50
89
  return;
51
90
  }
52
- const nextValue = updatedData[ field.id ];
91
+ const nextValue = field.getValue( { item: updatedData } );
53
92
  if ( fastDeepEqual( nextValue, currentValue ) ) {
54
93
  return;
55
94
  }
@@ -86,7 +86,7 @@ export const Default = ( {
86
86
  .filter(
87
87
  ( item ) => selection?.includes( String( item.id ) )
88
88
  )
89
- .map( ( item ) => item.title )
89
+ .map( ( item ) => item.name.title )
90
90
  .join( ', ' );
91
91
  // eslint-disable-next-line no-alert
92
92
  window.alert( selectedItemNames );
@@ -562,9 +562,10 @@ function FieldControl() {
562
562
  ( f ) =>
563
563
  ! visibleFieldIds.includes( f.id ) &&
564
564
  ! togglableFields.includes( f.id ) &&
565
- f.type !== 'media'
565
+ f.type !== 'media' &&
566
+ f.enableHiding !== false
566
567
  );
567
- const visibleFields = visibleFieldIds
568
+ let visibleFields = visibleFieldIds
568
569
  .map( ( fieldId ) => fields.find( ( f ) => f.id === fieldId ) )
569
570
  .filter( isDefined );
570
571
 
@@ -622,7 +623,7 @@ function FieldControl() {
622
623
  isVisibleFlag: 'showDescription',
623
624
  },
624
625
  ].filter( ( { field } ) => isDefined( field ) );
625
- const visibleLockedFields = lockedFields.filter(
626
+ let visibleLockedFields = lockedFields.filter(
626
627
  ( { field, isVisibleFlag } ) =>
627
628
  // @ts-expect-error
628
629
  isDefined( field ) && ( view[ isVisibleFlag ] ?? true )
@@ -631,6 +632,20 @@ function FieldControl() {
631
632
  isVisibleFlag: string;
632
633
  ui?: ReactNode;
633
634
  } >;
635
+
636
+ // If only one locked field is visible, prevent it from being hidden.
637
+ if ( visibleLockedFields.length === 1 ) {
638
+ visibleLockedFields = visibleLockedFields.map( ( locked ) => ( {
639
+ ...locked,
640
+ field: { ...locked.field, enableHiding: false },
641
+ } ) );
642
+ }
643
+
644
+ // If no locked fields are visible but there are visibleFields, lock the last visible field.
645
+ if ( visibleLockedFields.length === 0 && visibleFields.length === 1 ) {
646
+ visibleFields = [ { ...visibleFields[ 0 ], enableHiding: false } ];
647
+ }
648
+
634
649
  const hiddenLockedFields = lockedFields.filter(
635
650
  ( { field, isVisibleFlag } ) =>
636
651
  // @ts-expect-error
@@ -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
  }