@wordpress/dataviews 12.0.1-next.v.202602091733.0 → 12.0.1

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 (260) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/README.md +3 -1
  3. package/build/components/dataform-controls/color.cjs +19 -30
  4. package/build/components/dataform-controls/color.cjs.map +3 -3
  5. package/build/components/dataform-controls/date.cjs.map +1 -1
  6. package/build/components/dataform-controls/datetime.cjs +12 -21
  7. package/build/components/dataform-controls/datetime.cjs.map +2 -2
  8. package/build/components/dataform-layouts/card/index.cjs +112 -164
  9. package/build/components/dataform-layouts/card/index.cjs.map +3 -3
  10. package/build/components/dataform-layouts/index.cjs +1 -1
  11. package/build/components/dataform-layouts/index.cjs.map +1 -1
  12. package/build/components/dataform-layouts/normalize-form.cjs +2 -1
  13. package/build/components/dataform-layouts/normalize-form.cjs.map +2 -2
  14. package/build/components/dataform-layouts/panel/dropdown.cjs +79 -65
  15. package/build/components/dataform-layouts/panel/dropdown.cjs.map +3 -3
  16. package/build/components/dataform-layouts/panel/index.cjs +13 -177
  17. package/build/components/dataform-layouts/panel/index.cjs.map +3 -3
  18. package/build/components/dataform-layouts/panel/modal.cjs +12 -9
  19. package/build/components/dataform-layouts/panel/modal.cjs.map +3 -3
  20. package/build/components/dataform-layouts/panel/summary-button.cjs +125 -56
  21. package/build/components/dataform-layouts/panel/summary-button.cjs.map +3 -3
  22. package/build/components/dataform-layouts/panel/utils/get-first-validation-error.cjs +59 -0
  23. package/build/components/dataform-layouts/panel/utils/get-first-validation-error.cjs.map +7 -0
  24. package/build/components/dataform-layouts/panel/utils/get-label-classname.cjs +45 -0
  25. package/build/components/dataform-layouts/panel/utils/get-label-classname.cjs.map +7 -0
  26. package/build/components/dataform-layouts/panel/utils/get-label-content.cjs +36 -0
  27. package/build/components/dataform-layouts/panel/utils/get-label-content.cjs.map +7 -0
  28. package/build/components/dataform-layouts/panel/utils/use-field-from-form-field.cjs +77 -0
  29. package/build/components/dataform-layouts/panel/utils/use-field-from-form-field.cjs.map +7 -0
  30. package/build/components/dataviews-bulk-actions/index.cjs +2 -2
  31. package/build/components/dataviews-bulk-actions/index.cjs.map +2 -2
  32. package/build/components/dataviews-context/index.cjs +1 -0
  33. package/build/components/dataviews-context/index.cjs.map +2 -2
  34. package/build/components/dataviews-filters/filter.cjs.map +1 -1
  35. package/build/components/dataviews-filters/search-widget.cjs +25 -11
  36. package/build/components/dataviews-filters/search-widget.cjs.map +2 -2
  37. package/build/components/dataviews-filters/toggle.cjs +6 -2
  38. package/build/components/dataviews-filters/toggle.cjs.map +2 -2
  39. package/build/components/dataviews-footer/index.cjs +28 -12
  40. package/build/components/dataviews-footer/index.cjs.map +3 -3
  41. package/build/components/dataviews-item-actions/index.cjs +0 -1
  42. package/build/components/dataviews-item-actions/index.cjs.map +2 -2
  43. package/build/components/dataviews-layout/index.cjs +12 -0
  44. package/build/components/dataviews-layout/index.cjs.map +2 -2
  45. package/build/components/dataviews-layouts/activity/index.cjs +41 -26
  46. package/build/components/dataviews-layouts/activity/index.cjs.map +3 -3
  47. package/build/components/dataviews-layouts/grid/composite-grid.cjs +2 -0
  48. package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
  49. package/build/components/dataviews-layouts/grid/index.cjs +19 -14
  50. package/build/components/dataviews-layouts/grid/index.cjs.map +3 -3
  51. package/build/components/dataviews-layouts/list/index.cjs +13 -9
  52. package/build/components/dataviews-layouts/list/index.cjs.map +2 -2
  53. package/build/components/dataviews-layouts/picker-table/index.cjs +1 -1
  54. package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
  55. package/build/components/dataviews-layouts/table/index.cjs +40 -19
  56. package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
  57. package/build/dataviews/index.cjs +9 -2
  58. package/build/dataviews/index.cjs.map +3 -3
  59. package/build/dataviews-picker/index.cjs +1 -0
  60. package/build/dataviews-picker/index.cjs.map +2 -2
  61. package/build/hooks/use-data.cjs +46 -0
  62. package/build/hooks/use-data.cjs.map +7 -0
  63. package/build/hooks/use-delayed-loading.cjs +47 -0
  64. package/build/hooks/use-delayed-loading.cjs.map +7 -0
  65. package/build/types/dataform.cjs.map +1 -1
  66. package/build-module/components/dataform-controls/color.mjs +24 -31
  67. package/build-module/components/dataform-controls/color.mjs.map +2 -2
  68. package/build-module/components/dataform-controls/date.mjs.map +1 -1
  69. package/build-module/components/dataform-controls/datetime.mjs +13 -22
  70. package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
  71. package/build-module/components/dataform-layouts/card/index.mjs +113 -161
  72. package/build-module/components/dataform-layouts/card/index.mjs.map +3 -3
  73. package/build-module/components/dataform-layouts/index.mjs +1 -1
  74. package/build-module/components/dataform-layouts/index.mjs.map +1 -1
  75. package/build-module/components/dataform-layouts/normalize-form.mjs +2 -1
  76. package/build-module/components/dataform-layouts/normalize-form.mjs.map +2 -2
  77. package/build-module/components/dataform-layouts/panel/dropdown.mjs +81 -67
  78. package/build-module/components/dataform-layouts/panel/dropdown.mjs.map +2 -2
  79. package/build-module/components/dataform-layouts/panel/index.mjs +14 -178
  80. package/build-module/components/dataform-layouts/panel/index.mjs.map +2 -2
  81. package/build-module/components/dataform-layouts/panel/modal.mjs +12 -9
  82. package/build-module/components/dataform-layouts/panel/modal.mjs.map +2 -2
  83. package/build-module/components/dataform-layouts/panel/summary-button.mjs +117 -58
  84. package/build-module/components/dataform-layouts/panel/summary-button.mjs.map +2 -2
  85. package/build-module/components/dataform-layouts/panel/utils/get-first-validation-error.mjs +38 -0
  86. package/build-module/components/dataform-layouts/panel/utils/get-first-validation-error.mjs.map +7 -0
  87. package/build-module/components/dataform-layouts/panel/utils/get-label-classname.mjs +14 -0
  88. package/build-module/components/dataform-layouts/panel/utils/get-label-classname.mjs.map +7 -0
  89. package/build-module/components/dataform-layouts/panel/utils/get-label-content.mjs +15 -0
  90. package/build-module/components/dataform-layouts/panel/utils/get-label-content.mjs.map +7 -0
  91. package/build-module/components/dataform-layouts/panel/utils/use-field-from-form-field.mjs +46 -0
  92. package/build-module/components/dataform-layouts/panel/utils/use-field-from-form-field.mjs.map +7 -0
  93. package/build-module/components/dataviews-bulk-actions/index.mjs +2 -2
  94. package/build-module/components/dataviews-bulk-actions/index.mjs.map +2 -2
  95. package/build-module/components/dataviews-context/index.mjs +1 -0
  96. package/build-module/components/dataviews-context/index.mjs.map +2 -2
  97. package/build-module/components/dataviews-filters/filter.mjs.map +1 -1
  98. package/build-module/components/dataviews-filters/search-widget.mjs +25 -11
  99. package/build-module/components/dataviews-filters/search-widget.mjs.map +2 -2
  100. package/build-module/components/dataviews-filters/toggle.mjs +6 -2
  101. package/build-module/components/dataviews-filters/toggle.mjs.map +2 -2
  102. package/build-module/components/dataviews-footer/index.mjs +28 -12
  103. package/build-module/components/dataviews-footer/index.mjs.map +2 -2
  104. package/build-module/components/dataviews-item-actions/index.mjs +0 -1
  105. package/build-module/components/dataviews-item-actions/index.mjs.map +2 -2
  106. package/build-module/components/dataviews-layout/index.mjs +12 -0
  107. package/build-module/components/dataviews-layout/index.mjs.map +2 -2
  108. package/build-module/components/dataviews-layouts/activity/index.mjs +41 -26
  109. package/build-module/components/dataviews-layouts/activity/index.mjs.map +2 -2
  110. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +2 -0
  111. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
  112. package/build-module/components/dataviews-layouts/grid/index.mjs +19 -14
  113. package/build-module/components/dataviews-layouts/grid/index.mjs.map +2 -2
  114. package/build-module/components/dataviews-layouts/list/index.mjs +13 -9
  115. package/build-module/components/dataviews-layouts/list/index.mjs.map +2 -2
  116. package/build-module/components/dataviews-layouts/picker-table/index.mjs +1 -1
  117. package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
  118. package/build-module/components/dataviews-layouts/table/index.mjs +40 -19
  119. package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
  120. package/build-module/dataviews/index.mjs +9 -2
  121. package/build-module/dataviews/index.mjs.map +2 -2
  122. package/build-module/dataviews-picker/index.mjs +1 -0
  123. package/build-module/dataviews-picker/index.mjs.map +2 -2
  124. package/build-module/hooks/use-data.mjs +25 -0
  125. package/build-module/hooks/use-data.mjs.map +7 -0
  126. package/build-module/hooks/use-delayed-loading.mjs +22 -0
  127. package/build-module/hooks/use-delayed-loading.mjs.map +7 -0
  128. package/build-style/style-rtl.css +190 -31
  129. package/build-style/style.css +190 -31
  130. package/build-types/components/dataform-controls/color.d.ts.map +1 -1
  131. package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
  132. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts +16 -0
  133. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts.map +1 -0
  134. package/build-types/components/dataform-layouts/card/index.d.ts +1 -10
  135. package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
  136. package/build-types/components/dataform-layouts/normalize-form.d.ts.map +1 -1
  137. package/build-types/components/dataform-layouts/panel/dropdown.d.ts +2 -13
  138. package/build-types/components/dataform-layouts/panel/dropdown.d.ts.map +1 -1
  139. package/build-types/components/dataform-layouts/panel/index.d.ts +1 -1
  140. package/build-types/components/dataform-layouts/panel/index.d.ts.map +1 -1
  141. package/build-types/components/dataform-layouts/panel/modal.d.ts +2 -11
  142. package/build-types/components/dataform-layouts/panel/modal.d.ts.map +1 -1
  143. package/build-types/components/dataform-layouts/panel/summary-button.d.ts +6 -5
  144. package/build-types/components/dataform-layouts/panel/summary-button.d.ts.map +1 -1
  145. package/build-types/components/dataform-layouts/panel/utils/get-first-validation-error.d.ts +4 -0
  146. package/build-types/components/dataform-layouts/panel/utils/get-first-validation-error.d.ts.map +1 -0
  147. package/build-types/components/dataform-layouts/panel/utils/get-label-classname.d.ts +4 -0
  148. package/build-types/components/dataform-layouts/panel/utils/get-label-classname.d.ts.map +1 -0
  149. package/build-types/components/dataform-layouts/panel/utils/get-label-content.d.ts +3 -0
  150. package/build-types/components/dataform-layouts/panel/utils/get-label-content.d.ts.map +1 -0
  151. package/build-types/components/dataform-layouts/panel/utils/use-field-from-form-field.d.ts +23 -0
  152. package/build-types/components/dataform-layouts/panel/utils/use-field-from-form-field.d.ts.map +1 -0
  153. package/build-types/components/dataviews-context/index.d.ts +1 -0
  154. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  155. package/build-types/components/dataviews-filters/filter.d.ts +1 -1
  156. package/build-types/components/dataviews-filters/filter.d.ts.map +1 -1
  157. package/build-types/components/dataviews-filters/search-widget.d.ts.map +1 -1
  158. package/build-types/components/dataviews-filters/toggle.d.ts.map +1 -1
  159. package/build-types/components/dataviews-footer/index.d.ts +1 -1
  160. package/build-types/components/dataviews-footer/index.d.ts.map +1 -1
  161. package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
  162. package/build-types/components/dataviews-layout/index.d.ts +1 -1
  163. package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
  164. package/build-types/components/dataviews-layouts/activity/index.d.ts.map +1 -1
  165. package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts +2 -1
  166. package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts.map +1 -1
  167. package/build-types/components/dataviews-layouts/grid/index.d.ts.map +1 -1
  168. package/build-types/components/dataviews-layouts/list/index.d.ts.map +1 -1
  169. package/build-types/components/dataviews-layouts/picker-activity/index.d.ts +3 -0
  170. package/build-types/components/dataviews-layouts/picker-activity/index.d.ts.map +1 -0
  171. package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
  172. package/build-types/components/dataviews-layouts/table/use-scroll-state.d.ts +25 -0
  173. package/build-types/components/dataviews-layouts/table/use-scroll-state.d.ts.map +1 -0
  174. package/build-types/components/dataviews-layouts/utils/grid-config-options.d.ts +2 -0
  175. package/build-types/components/dataviews-layouts/utils/grid-config-options.d.ts.map +1 -0
  176. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts +22 -0
  177. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts.map +1 -0
  178. package/build-types/dataform/stories/index.story.d.ts +9 -1
  179. package/build-types/dataform/stories/index.story.d.ts.map +1 -1
  180. package/build-types/dataform/stories/layout-panel.d.ts +3 -1
  181. package/build-types/dataform/stories/layout-panel.d.ts.map +1 -1
  182. package/build-types/dataviews/index.d.ts.map +1 -1
  183. package/build-types/dataviews/stories/empty.d.ts.map +1 -1
  184. package/build-types/dataviews/stories/fixtures.d.ts +1 -0
  185. package/build-types/dataviews/stories/fixtures.d.ts.map +1 -1
  186. package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
  187. package/build-types/dataviews/stories/layout-table.d.ts.map +1 -1
  188. package/build-types/dataviews/stories/minimal-ui.d.ts.map +1 -1
  189. package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
  190. package/build-types/dataviews-picker/index.d.ts.map +1 -1
  191. package/build-types/field-types/utils/is-valid-date-boundary.d.ts +7 -0
  192. package/build-types/field-types/utils/is-valid-date-boundary.d.ts.map +1 -0
  193. package/build-types/hooks/test/use-data.d.ts +2 -0
  194. package/build-types/hooks/test/use-data.d.ts.map +1 -0
  195. package/build-types/hooks/use-data.d.ts +12 -0
  196. package/build-types/hooks/use-data.d.ts.map +1 -0
  197. package/build-types/hooks/use-delayed-loading.d.ts +4 -0
  198. package/build-types/hooks/use-delayed-loading.d.ts.map +1 -0
  199. package/build-types/hooks/use-infinite-scroll.d.ts +21 -0
  200. package/build-types/hooks/use-infinite-scroll.d.ts.map +1 -0
  201. package/build-types/hooks/use-selected-items.d.ts +19 -0
  202. package/build-types/hooks/use-selected-items.d.ts.map +1 -0
  203. package/build-types/types/dataform.d.ts +3 -0
  204. package/build-types/types/dataform.d.ts.map +1 -1
  205. package/build-wp/index.js +2898 -2198
  206. package/package.json +21 -21
  207. package/src/components/dataform-controls/color.tsx +29 -36
  208. package/src/components/dataform-controls/date.tsx +2 -2
  209. package/src/components/dataform-controls/datetime.tsx +19 -34
  210. package/src/components/dataform-controls/style.scss +4 -0
  211. package/src/components/dataform-layouts/card/index.tsx +118 -185
  212. package/src/components/dataform-layouts/index.tsx +1 -1
  213. package/src/components/dataform-layouts/normalize-form.ts +1 -0
  214. package/src/components/dataform-layouts/panel/dropdown.tsx +95 -96
  215. package/src/components/dataform-layouts/panel/index.tsx +10 -245
  216. package/src/components/dataform-layouts/panel/modal.tsx +16 -20
  217. package/src/components/dataform-layouts/panel/style.scss +110 -27
  218. package/src/components/dataform-layouts/panel/summary-button.tsx +140 -62
  219. package/src/components/dataform-layouts/panel/utils/get-first-validation-error.ts +47 -0
  220. package/src/components/dataform-layouts/panel/utils/get-label-classname.ts +18 -0
  221. package/src/components/dataform-layouts/panel/utils/get-label-content.tsx +24 -0
  222. package/src/components/dataform-layouts/panel/utils/use-field-from-form-field.ts +78 -0
  223. package/src/components/dataform-layouts/regular/style.scss +7 -0
  224. package/src/components/dataform-layouts/test/normalize-form.ts +5 -0
  225. package/src/components/dataviews-bulk-actions/index.tsx +2 -2
  226. package/src/components/dataviews-context/index.ts +2 -0
  227. package/src/components/dataviews-filters/filter.tsx +1 -1
  228. package/src/components/dataviews-filters/search-widget.tsx +10 -2
  229. package/src/components/dataviews-filters/style.scss +8 -0
  230. package/src/components/dataviews-filters/toggle.tsx +10 -3
  231. package/src/components/dataviews-footer/index.tsx +39 -12
  232. package/src/components/dataviews-footer/style.scss +6 -1
  233. package/src/components/dataviews-item-actions/index.tsx +0 -3
  234. package/src/components/dataviews-item-actions/style.scss +7 -0
  235. package/src/components/dataviews-layout/index.tsx +24 -0
  236. package/src/components/dataviews-layouts/activity/index.tsx +29 -22
  237. package/src/components/dataviews-layouts/activity/style.scss +5 -0
  238. package/src/components/dataviews-layouts/grid/composite-grid.tsx +5 -1
  239. package/src/components/dataviews-layouts/grid/index.tsx +19 -22
  240. package/src/components/dataviews-layouts/grid/style.scss +5 -0
  241. package/src/components/dataviews-layouts/list/index.tsx +18 -18
  242. package/src/components/dataviews-layouts/list/style.scss +5 -0
  243. package/src/components/dataviews-layouts/picker-table/index.tsx +1 -1
  244. package/src/components/dataviews-layouts/table/index.tsx +48 -22
  245. package/src/components/dataviews-layouts/table/style.scss +8 -2
  246. package/src/dataform/stories/index.story.tsx +5 -0
  247. package/src/dataform/stories/layout-panel.tsx +19 -2
  248. package/src/dataform/test/dataform.tsx +2 -2
  249. package/src/dataviews/index.tsx +11 -3
  250. package/src/dataviews/stories/empty.tsx +1 -7
  251. package/src/dataviews/stories/fixtures.tsx +89 -0
  252. package/src/dataviews/stories/free-composition.tsx +0 -5
  253. package/src/dataviews/stories/layout-table.tsx +1 -7
  254. package/src/dataviews/stories/minimal-ui.tsx +0 -5
  255. package/src/dataviews/stories/with-card.tsx +1 -7
  256. package/src/dataviews/style.scss +25 -0
  257. package/src/dataviews-picker/index.tsx +2 -1
  258. package/src/hooks/use-data.ts +45 -0
  259. package/src/hooks/use-delayed-loading.ts +21 -0
  260. package/src/types/dataform.ts +4 -0
@@ -8,25 +8,24 @@ import {
8
8
  Button,
9
9
  } from '@wordpress/components';
10
10
  import { __ } from '@wordpress/i18n';
11
- import { useMemo, useRef } from '@wordpress/element';
11
+ import { useMemo, useRef, useState } from '@wordpress/element';
12
12
  import { closeSmall } from '@wordpress/icons';
13
- import { useFocusOnMount } from '@wordpress/compose';
13
+ import { __experimentalUseDialog as useDialog } from '@wordpress/compose';
14
14
  import { Stack } from '@wordpress/ui';
15
15
 
16
16
  /**
17
17
  * Internal dependencies
18
18
  */
19
19
  import type {
20
- FieldValidity,
20
+ FieldLayoutProps,
21
21
  NormalizedForm,
22
- NormalizedFormField,
23
22
  FormValidity,
24
- NormalizedField,
25
23
  } from '../../../types';
26
24
  import { DataFormLayout } from '../data-form-layout';
27
25
  import { DEFAULT_LAYOUT } from '../normalize-form';
28
26
  import SummaryButton from './summary-button';
29
27
  import useReportValidity from '../../../hooks/use-report-validity';
28
+ import useFieldFromFormField from './utils/use-field-from-form-field';
30
29
 
31
30
  function DropdownHeader( {
32
31
  title,
@@ -78,25 +77,29 @@ function PanelDropdown< Item >( {
78
77
  field,
79
78
  onChange,
80
79
  validity,
81
- labelPosition = 'side',
82
- summaryFields,
83
- fieldDefinition,
84
- popoverAnchor,
85
- onClose: onCloseCallback,
86
- touched,
87
- }: {
88
- data: Item;
89
- field: NormalizedFormField;
90
- onChange: ( value: any ) => void;
91
- validity?: FieldValidity;
92
- labelPosition: 'side' | 'top' | 'none';
93
- summaryFields: NormalizedField< Item >[];
94
- fieldDefinition: NormalizedField< Item >;
95
- popoverAnchor: HTMLElement | null;
96
- onClose?: () => void;
97
- touched: boolean;
98
- } ) {
99
- const fieldLabel = !! field.children ? field.label : fieldDefinition?.label;
80
+ }: FieldLayoutProps< Item > ) {
81
+ const [ touched, setTouched ] = useState( false );
82
+
83
+ // Use internal state instead of a ref to make sure that the component
84
+ // re-renders when the popover's anchor updates.
85
+ const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >(
86
+ null
87
+ );
88
+ // Memoize popoverProps to avoid returning a new object every time.
89
+ const popoverProps = useMemo(
90
+ () => ( {
91
+ // Anchor the popover to the middle of the entire row so that it doesn't
92
+ // move around when the label changes.
93
+ anchor: popoverAnchor,
94
+ placement: 'left-start',
95
+ offset: 36,
96
+ shift: true,
97
+ } ),
98
+ [ popoverAnchor ]
99
+ );
100
+ const [ dialogRef, dialogProps ] = useDialog( {
101
+ focusOnMount: 'firstInputElement',
102
+ } );
100
103
 
101
104
  const form: NormalizedForm = useMemo(
102
105
  () => ( {
@@ -120,80 +123,76 @@ function PanelDropdown< Item >( {
120
123
  return { [ field.id ]: validity };
121
124
  }, [ validity, field ] );
122
125
 
123
- // Memoize popoverProps to avoid returning a new object every time.
124
- const popoverProps = useMemo(
125
- () => ( {
126
- // Anchor the popover to the middle of the entire row so that it doesn't
127
- // move around when the label changes.
128
- anchor: popoverAnchor,
129
- placement: 'left-start',
130
- offset: 36,
131
- shift: true,
132
- } ),
133
- [ popoverAnchor ]
134
- );
135
-
136
- const focusOnMountRef = useFocusOnMount( 'firstInputElement' );
126
+ const { fieldDefinition, fieldLabel, summaryFields } =
127
+ useFieldFromFormField( field );
128
+ if ( ! fieldDefinition ) {
129
+ return null;
130
+ }
137
131
 
138
132
  return (
139
- <Dropdown
140
- contentClassName="dataforms-layouts-panel__field-dropdown"
141
- popoverProps={ popoverProps }
142
- focusOnMount={ false }
143
- onToggle={ ( willOpen ) => {
144
- if ( ! willOpen ) {
145
- onCloseCallback?.();
146
- }
147
- } }
148
- toggleProps={ {
149
- size: 'compact',
150
- variant: 'tertiary',
151
- tooltipPosition: 'middle left',
152
- } }
153
- renderToggle={ ( { isOpen, onToggle } ) => (
154
- <SummaryButton
155
- summaryFields={ summaryFields }
156
- data={ data }
157
- labelPosition={ labelPosition }
158
- fieldLabel={ fieldLabel }
159
- disabled={ fieldDefinition.readOnly === true }
160
- onClick={ onToggle }
161
- aria-expanded={ isOpen }
162
- />
163
- ) }
164
- renderContent={ ( { onClose } ) => (
165
- <DropdownContentWithValidation touched={ touched }>
166
- <DropdownHeader title={ fieldLabel } onClose={ onClose } />
167
- <div ref={ focusOnMountRef }>
168
- <DataFormLayout
169
- data={ data }
170
- form={ form }
171
- onChange={ onChange }
172
- validity={ formValidity }
173
- >
174
- { (
175
- FieldLayout,
176
- childField,
177
- childFieldValidity,
178
- markWhenOptional
179
- ) => (
180
- <FieldLayout
181
- key={ childField.id }
182
- data={ data }
183
- field={ childField }
184
- onChange={ onChange }
185
- hideLabelFromVision={
186
- ( form?.fields ?? [] ).length < 2
187
- }
188
- markWhenOptional={ markWhenOptional }
189
- validity={ childFieldValidity }
190
- />
191
- ) }
192
- </DataFormLayout>
193
- </div>
194
- </DropdownContentWithValidation>
195
- ) }
196
- />
133
+ <div
134
+ ref={ setPopoverAnchor }
135
+ className="dataforms-layouts-panel__field-dropdown-anchor"
136
+ >
137
+ <Dropdown
138
+ contentClassName="dataforms-layouts-panel__field-dropdown"
139
+ popoverProps={ popoverProps }
140
+ focusOnMount={ false }
141
+ onToggle={ ( willOpen ) => {
142
+ if ( ! willOpen ) {
143
+ setTouched( true );
144
+ }
145
+ } }
146
+ renderToggle={ ( { isOpen, onToggle } ) => (
147
+ <SummaryButton
148
+ data={ data }
149
+ field={ field }
150
+ fieldLabel={ fieldLabel }
151
+ summaryFields={ summaryFields }
152
+ validity={ validity }
153
+ touched={ touched }
154
+ disabled={ fieldDefinition.readOnly === true }
155
+ onClick={ onToggle }
156
+ aria-expanded={ isOpen }
157
+ />
158
+ ) }
159
+ renderContent={ ( { onClose } ) => (
160
+ <DropdownContentWithValidation touched={ touched }>
161
+ <div ref={ dialogRef } { ...dialogProps }>
162
+ <DropdownHeader
163
+ title={ fieldLabel }
164
+ onClose={ onClose }
165
+ />
166
+ <DataFormLayout
167
+ data={ data }
168
+ form={ form }
169
+ onChange={ onChange }
170
+ validity={ formValidity }
171
+ >
172
+ { (
173
+ FieldLayout,
174
+ childField,
175
+ childFieldValidity,
176
+ markWhenOptional
177
+ ) => (
178
+ <FieldLayout
179
+ key={ childField.id }
180
+ data={ data }
181
+ field={ childField }
182
+ onChange={ onChange }
183
+ hideLabelFromVision={
184
+ ( form?.fields ?? [] ).length < 2
185
+ }
186
+ markWhenOptional={ markWhenOptional }
187
+ validity={ childFieldValidity }
188
+ />
189
+ ) }
190
+ </DataFormLayout>
191
+ </div>
192
+ </DropdownContentWithValidation>
193
+ ) }
194
+ />
195
+ </div>
197
196
  );
198
197
  }
199
198
 
@@ -1,136 +1,9 @@
1
- /**
2
- * External dependencies
3
- */
4
- import clsx from 'clsx';
5
-
6
- /**
7
- * WordPress dependencies
8
- */
9
- import { Icon, Tooltip } from '@wordpress/components';
10
- import { useState, useContext } from '@wordpress/element';
11
- import { error as errorIcon } from '@wordpress/icons';
12
- import { Stack } from '@wordpress/ui';
13
-
14
1
  /**
15
2
  * Internal dependencies
16
3
  */
17
- import type {
18
- FieldLayoutProps,
19
- FieldValidity,
20
- NormalizedField,
21
- NormalizedFormField,
22
- NormalizedPanelLayout,
23
- } from '../../../types';
24
- import DataFormContext from '../../dataform-context';
25
- import PanelDropdown from './dropdown';
4
+ import type { FieldLayoutProps, NormalizedPanelLayout } from '../../../types';
26
5
  import PanelModal from './modal';
27
- import { getSummaryFields } from '../get-summary-fields';
28
-
29
- function getFirstValidationError(
30
- validity: FieldValidity | undefined
31
- ): string | undefined {
32
- if ( ! validity ) {
33
- return undefined;
34
- }
35
-
36
- const validityRules = Object.keys( validity ).filter(
37
- ( key ) => key !== 'children'
38
- );
39
-
40
- for ( const key of validityRules ) {
41
- const rule = validity[ key as keyof Omit< FieldValidity, 'children' > ];
42
- if ( rule === undefined ) {
43
- continue;
44
- }
45
-
46
- if ( rule.type === 'invalid' ) {
47
- if ( rule.message ) {
48
- return rule.message;
49
- }
50
-
51
- // Provide default message for required validation (message is optional)
52
- if ( key === 'required' ) {
53
- return 'A required field is empty';
54
- }
55
-
56
- return 'Unidentified validation error';
57
- }
58
- }
59
-
60
- // Check children recursively
61
- if ( validity.children ) {
62
- for ( const childValidity of Object.values( validity.children ) ) {
63
- const childError = getFirstValidationError( childValidity );
64
- if ( childError ) {
65
- return childError;
66
- }
67
- }
68
- }
69
-
70
- return undefined;
71
- }
72
-
73
- const getFieldDefinition = < Item, >(
74
- field: NormalizedFormField,
75
- fields: NormalizedField< Item >[]
76
- ) => {
77
- const fieldDefinition = fields.find( ( _field ) => _field.id === field.id );
78
-
79
- if ( ! fieldDefinition ) {
80
- return fields.find( ( _field ) => {
81
- if ( !! field.children ) {
82
- const simpleChildren = field.children.filter(
83
- ( child ) => ! child.children
84
- );
85
-
86
- if ( simpleChildren.length === 0 ) {
87
- return false;
88
- }
89
-
90
- return _field.id === simpleChildren[ 0 ].id;
91
- }
92
-
93
- return _field.id === field.id;
94
- } );
95
- }
96
-
97
- return fieldDefinition;
98
- };
99
-
100
- /**
101
- * Determines the field definition and summary fields for a panel layout.
102
- *
103
- * Summary fields are determined with the following priority:
104
- * 1. Use layout.summary fields if they exist
105
- * 2. Fall back to the field definition that matches the form field's id
106
- * 3. If the form field id doesn't exist, pick the first child field
107
- * 4. If no field definition is found, return empty summary fields
108
- *
109
- * @param layout - The normalized panel layout configuration
110
- * @param field - The form field to get definition for
111
- * @param fields - Array of normalized field definitions
112
- * @return Object containing fieldDefinition and summaryFields
113
- */
114
- const getFieldDefinitionAndSummaryFields = < Item, >(
115
- layout: NormalizedPanelLayout,
116
- field: NormalizedFormField,
117
- fields: NormalizedField< Item >[]
118
- ) => {
119
- const summaryFields = getSummaryFields( layout.summary, fields );
120
- const fieldDefinition = getFieldDefinition( field, fields );
121
-
122
- if ( summaryFields.length === 0 ) {
123
- return {
124
- summaryFields: fieldDefinition ? [ fieldDefinition ] : [],
125
- fieldDefinition,
126
- };
127
- }
128
-
129
- return {
130
- summaryFields,
131
- fieldDefinition,
132
- };
133
- };
6
+ import PanelDropdown from './dropdown';
134
7
 
135
8
  export default function FormPanelField< Item >( {
136
9
  data,
@@ -138,133 +11,25 @@ export default function FormPanelField< Item >( {
138
11
  onChange,
139
12
  validity,
140
13
  }: FieldLayoutProps< Item > ) {
141
- const { fields } = useContext( DataFormContext );
142
14
  const layout = field.layout as NormalizedPanelLayout;
143
15
 
144
- // Use internal state instead of a ref to make sure that the component
145
- // re-renders when the popover's anchor updates.
146
- const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >(
147
- null
148
- );
149
-
150
- // Track if the panel has been closed (touched) to only show errors after interaction.
151
- const [ touched, setTouched ] = useState( false );
152
- const handleClose = () => setTouched( true );
153
-
154
- const { fieldDefinition, summaryFields } =
155
- getFieldDefinitionAndSummaryFields( layout, field, fields );
156
-
157
- if ( ! fieldDefinition ) {
158
- return null;
159
- }
160
-
161
- const labelPosition = layout.labelPosition;
162
- const errorMessage = getFirstValidationError( validity );
163
- const showError = touched && !! errorMessage;
164
- const labelClassName = clsx(
165
- 'dataforms-layouts-panel__field-label',
166
- `dataforms-layouts-panel__field-label--label-position-${ labelPosition }`,
167
- { 'has-error': showError }
168
- );
169
- const fieldLabel = !! field.children ? field.label : fieldDefinition?.label;
170
-
171
- const labelContent = showError ? (
172
- <Tooltip text={ errorMessage } placement="top">
173
- <Stack
174
- direction="row"
175
- gap="sm"
176
- className="dataforms-layouts-panel__field-label-error-content"
177
- justify="flex-start"
178
- >
179
- <Icon icon={ errorIcon } size={ 16 } />
180
- <>{ fieldLabel }</>
181
- </Stack>
182
- </Tooltip>
183
- ) : (
184
- fieldLabel
185
- );
186
-
187
- const renderedControl =
188
- layout.openAs === 'modal' ? (
16
+ if ( layout.openAs === 'modal' ) {
17
+ return (
189
18
  <PanelModal
190
- data={ data }
191
- field={ field }
192
- onChange={ onChange }
193
- labelPosition={ labelPosition }
194
- summaryFields={ summaryFields }
195
- fieldDefinition={ fieldDefinition }
196
- onClose={ handleClose }
197
- touched={ touched }
198
- />
199
- ) : (
200
- <PanelDropdown
201
19
  data={ data }
202
20
  field={ field }
203
21
  onChange={ onChange }
204
22
  validity={ validity }
205
- labelPosition={ labelPosition }
206
- summaryFields={ summaryFields }
207
- fieldDefinition={ fieldDefinition }
208
- popoverAnchor={ popoverAnchor }
209
- onClose={ handleClose }
210
- touched={ touched }
211
23
  />
212
24
  );
213
-
214
- if ( labelPosition === 'top' ) {
215
- return (
216
- <Stack
217
- direction="column"
218
- className="dataforms-layouts-panel__field"
219
- >
220
- <div
221
- className={ labelClassName }
222
- style={ { paddingBottom: 0 } }
223
- >
224
- { labelContent }
225
- </div>
226
- <div className="dataforms-layouts-panel__field-control">
227
- { renderedControl }
228
- </div>
229
- </Stack>
230
- );
231
- }
232
-
233
- if ( labelPosition === 'none' ) {
234
- return (
235
- <Stack
236
- direction="row"
237
- gap="sm"
238
- className="dataforms-layouts-panel__field dataforms-layouts-panel__field--label-position-none"
239
- >
240
- { showError && (
241
- <Tooltip text={ errorMessage } placement="top">
242
- <Icon
243
- className="dataforms-layouts-panel__field-label-error-content"
244
- icon={ errorIcon }
245
- size={ 16 }
246
- />
247
- </Tooltip>
248
- ) }
249
- <div className="dataforms-layouts-panel__field-control">
250
- { renderedControl }
251
- </div>
252
- </Stack>
253
- );
254
25
  }
255
26
 
256
- // Defaults to label position side.
257
27
  return (
258
- <Stack
259
- direction="row"
260
- gap="sm"
261
- ref={ setPopoverAnchor }
262
- className="dataforms-layouts-panel__field"
263
- >
264
- <div className={ labelClassName }>{ labelContent }</div>
265
- <div className="dataforms-layouts-panel__field-control">
266
- { renderedControl }
267
- </div>
268
- </Stack>
28
+ <PanelDropdown
29
+ data={ data }
30
+ field={ field }
31
+ onChange={ onChange }
32
+ validity={ validity }
33
+ />
269
34
  );
270
35
  }
@@ -23,7 +23,7 @@ import type {
23
23
  Field,
24
24
  NormalizedForm,
25
25
  NormalizedFormField,
26
- NormalizedField,
26
+ FieldLayoutProps,
27
27
  } from '../../../types';
28
28
  import { DataFormLayout } from '../data-form-layout';
29
29
  import { DEFAULT_LAYOUT } from '../normalize-form';
@@ -31,6 +31,7 @@ import SummaryButton from './summary-button';
31
31
  import useFormValidity from '../../../hooks/use-form-validity';
32
32
  import useReportValidity from '../../../hooks/use-report-validity';
33
33
  import DataFormContext from '../../dataform-context';
34
+ import useFieldFromFormField from './utils/use-field-from-form-field';
34
35
 
35
36
  function ModalContent< Item >( {
36
37
  data,
@@ -164,37 +165,32 @@ function PanelModal< Item >( {
164
165
  data,
165
166
  field,
166
167
  onChange,
167
- labelPosition,
168
- summaryFields,
169
- fieldDefinition,
170
- onClose: onCloseCallback,
171
- touched,
172
- }: {
173
- data: Item;
174
- field: NormalizedFormField;
175
- onChange: ( value: any ) => void;
176
- labelPosition: 'side' | 'top' | 'none';
177
- summaryFields: NormalizedField< Item >[];
178
- fieldDefinition: NormalizedField< Item >;
179
- onClose?: () => void;
180
- touched: boolean;
181
- } ) {
168
+ validity,
169
+ }: FieldLayoutProps< Item > ) {
170
+ const [ touched, setTouched ] = useState( false );
171
+
182
172
  const [ isOpen, setIsOpen ] = useState( false );
183
173
 
184
- const fieldLabel = !! field.children ? field.label : fieldDefinition?.label;
174
+ const { fieldDefinition, fieldLabel, summaryFields } =
175
+ useFieldFromFormField( field );
176
+ if ( ! fieldDefinition ) {
177
+ return null;
178
+ }
185
179
 
186
180
  const handleClose = () => {
187
181
  setIsOpen( false );
188
- onCloseCallback?.();
182
+ setTouched( true );
189
183
  };
190
184
 
191
185
  return (
192
186
  <>
193
187
  <SummaryButton
194
- summaryFields={ summaryFields }
195
188
  data={ data }
196
- labelPosition={ labelPosition }
189
+ field={ field }
197
190
  fieldLabel={ fieldLabel }
191
+ summaryFields={ summaryFields }
192
+ validity={ validity }
193
+ touched={ touched }
198
194
  disabled={ fieldDefinition.readOnly === true }
199
195
  onClick={ () => setIsOpen( true ) }
200
196
  aria-expanded={ isOpen }