@wordpress/dataviews 13.1.1-next.v.202603161435.0 → 14.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (370) hide show
  1. package/CHANGELOG.md +36 -6
  2. package/README.md +33 -7
  3. package/build/components/dataform-controls/array.cjs +2 -0
  4. package/build/components/dataform-controls/array.cjs.map +2 -2
  5. package/build/components/dataform-controls/checkbox.cjs +3 -1
  6. package/build/components/dataform-controls/checkbox.cjs.map +2 -2
  7. package/build/components/dataform-controls/color.cjs +8 -2
  8. package/build/components/dataform-controls/color.cjs.map +2 -2
  9. package/build/components/dataform-controls/date.cjs +31 -9
  10. package/build/components/dataform-controls/date.cjs.map +3 -3
  11. package/build/components/dataform-controls/datetime.cjs +17 -6
  12. package/build/components/dataform-controls/datetime.cjs.map +3 -3
  13. package/build/components/dataform-controls/password.cjs +4 -1
  14. package/build/components/dataform-controls/password.cjs.map +2 -2
  15. package/build/components/dataform-controls/radio.cjs +3 -1
  16. package/build/components/dataform-controls/radio.cjs.map +2 -2
  17. package/build/components/dataform-controls/select.cjs +3 -1
  18. package/build/components/dataform-controls/select.cjs.map +2 -2
  19. package/build/components/dataform-controls/textarea.cjs +2 -0
  20. package/build/components/dataform-controls/textarea.cjs.map +2 -2
  21. package/build/components/dataform-controls/toggle-group.cjs +3 -1
  22. package/build/components/dataform-controls/toggle-group.cjs.map +2 -2
  23. package/build/components/dataform-controls/toggle.cjs +3 -1
  24. package/build/components/dataform-controls/toggle.cjs.map +2 -2
  25. package/build/components/dataform-controls/utils/relative-date-control.cjs +5 -2
  26. package/build/components/dataform-controls/utils/relative-date-control.cjs.map +2 -2
  27. package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs +48 -0
  28. package/build/components/dataform-controls/utils/use-disabled-date-matchers.cjs.map +7 -0
  29. package/build/components/dataform-controls/utils/validated-input.cjs +2 -0
  30. package/build/components/dataform-controls/utils/validated-input.cjs.map +2 -2
  31. package/build/components/dataform-controls/utils/validated-number.cjs +3 -1
  32. package/build/components/dataform-controls/utils/validated-number.cjs.map +2 -2
  33. package/build/components/dataform-layouts/card/index.cjs +132 -128
  34. package/build/components/dataform-layouts/card/index.cjs.map +3 -3
  35. package/build/components/dataform-layouts/panel/summary-button.cjs +0 -1
  36. package/build/components/dataform-layouts/panel/summary-button.cjs.map +2 -2
  37. package/build/components/dataviews-bulk-actions/index.cjs +28 -5
  38. package/build/components/dataviews-bulk-actions/index.cjs.map +2 -2
  39. package/build/components/dataviews-context/index.cjs +2 -2
  40. package/build/components/dataviews-context/index.cjs.map +2 -2
  41. package/build/components/dataviews-filters/input-widget.cjs +4 -0
  42. package/build/components/dataviews-filters/input-widget.cjs.map +2 -2
  43. package/build/components/dataviews-footer/index.cjs +2 -3
  44. package/build/components/dataviews-footer/index.cjs.map +2 -2
  45. package/build/components/dataviews-layouts/grid/composite-grid.cjs +378 -249
  46. package/build/components/dataviews-layouts/grid/composite-grid.cjs.map +2 -2
  47. package/build/components/dataviews-layouts/picker-grid/index.cjs +60 -30
  48. package/build/components/dataviews-layouts/picker-grid/index.cjs.map +2 -2
  49. package/build/components/dataviews-layouts/picker-table/index.cjs +45 -30
  50. package/build/components/dataviews-layouts/picker-table/index.cjs.map +2 -2
  51. package/build/components/dataviews-layouts/table/index.cjs +0 -1
  52. package/build/components/dataviews-layouts/table/index.cjs.map +2 -2
  53. package/build/components/dataviews-layouts/utils/use-infinite-scroll.cjs +62 -0
  54. package/build/components/dataviews-layouts/utils/use-infinite-scroll.cjs.map +7 -0
  55. package/build/components/dataviews-pagination/index.cjs +1 -0
  56. package/build/components/dataviews-pagination/index.cjs.map +2 -2
  57. package/build/components/dataviews-picker-footer/index.cjs +23 -4
  58. package/build/components/dataviews-picker-footer/index.cjs.map +2 -2
  59. package/build/components/dataviews-search/index.cjs +2 -1
  60. package/build/components/dataviews-search/index.cjs.map +2 -2
  61. package/build/components/dataviews-selection-checkbox/index.cjs +3 -2
  62. package/build/components/dataviews-selection-checkbox/index.cjs.map +2 -2
  63. package/build/components/dataviews-view-config/index.cjs +0 -2
  64. package/build/components/dataviews-view-config/index.cjs.map +3 -3
  65. package/build/components/dataviews-view-config/infinite-scroll-toggle.cjs +0 -3
  66. package/build/components/dataviews-view-config/infinite-scroll-toggle.cjs.map +2 -2
  67. package/build/dataviews/index.cjs +47 -45
  68. package/build/dataviews/index.cjs.map +3 -3
  69. package/build/dataviews-picker/index.cjs +41 -33
  70. package/build/dataviews-picker/index.cjs.map +3 -3
  71. package/build/field-types/date.cjs +4 -1
  72. package/build/field-types/date.cjs.map +2 -2
  73. package/build/field-types/datetime.cjs +4 -1
  74. package/build/field-types/datetime.cjs.map +2 -2
  75. package/build/field-types/index.cjs +1 -0
  76. package/build/field-types/index.cjs.map +2 -2
  77. package/build/field-types/utils/get-is-valid.cjs +29 -24
  78. package/build/field-types/utils/get-is-valid.cjs.map +2 -2
  79. package/build/field-types/utils/is-valid-date-boundary.cjs +64 -0
  80. package/build/field-types/utils/is-valid-date-boundary.cjs.map +7 -0
  81. package/build/hooks/index.cjs +11 -2
  82. package/build/hooks/index.cjs.map +2 -2
  83. package/build/hooks/use-data.cjs +146 -9
  84. package/build/hooks/use-data.cjs.map +2 -2
  85. package/build/hooks/use-infinite-scroll.cjs +208 -0
  86. package/build/hooks/use-infinite-scroll.cjs.map +7 -0
  87. package/build/hooks/use-selected-items.cjs +57 -0
  88. package/build/hooks/use-selected-items.cjs.map +7 -0
  89. package/build/types/dataviews.cjs.map +1 -1
  90. package/build/types/field-api.cjs.map +1 -1
  91. package/build/utils/filter-sort-and-paginate.cjs +5 -1
  92. package/build/utils/filter-sort-and-paginate.cjs.map +2 -2
  93. package/build/utils/get-footer-message.cjs +8 -8
  94. package/build/utils/get-footer-message.cjs.map +2 -2
  95. package/build-module/components/dataform-controls/array.mjs +2 -0
  96. package/build-module/components/dataform-controls/array.mjs.map +2 -2
  97. package/build-module/components/dataform-controls/checkbox.mjs +3 -1
  98. package/build-module/components/dataform-controls/checkbox.mjs.map +2 -2
  99. package/build-module/components/dataform-controls/color.mjs +8 -2
  100. package/build-module/components/dataform-controls/color.mjs.map +2 -2
  101. package/build-module/components/dataform-controls/date.mjs +31 -9
  102. package/build-module/components/dataform-controls/date.mjs.map +2 -2
  103. package/build-module/components/dataform-controls/datetime.mjs +17 -6
  104. package/build-module/components/dataform-controls/datetime.mjs.map +2 -2
  105. package/build-module/components/dataform-controls/password.mjs +4 -1
  106. package/build-module/components/dataform-controls/password.mjs.map +2 -2
  107. package/build-module/components/dataform-controls/radio.mjs +3 -1
  108. package/build-module/components/dataform-controls/radio.mjs.map +2 -2
  109. package/build-module/components/dataform-controls/select.mjs +3 -1
  110. package/build-module/components/dataform-controls/select.mjs.map +2 -2
  111. package/build-module/components/dataform-controls/textarea.mjs +2 -0
  112. package/build-module/components/dataform-controls/textarea.mjs.map +2 -2
  113. package/build-module/components/dataform-controls/toggle-group.mjs +3 -1
  114. package/build-module/components/dataform-controls/toggle-group.mjs.map +2 -2
  115. package/build-module/components/dataform-controls/toggle.mjs +3 -1
  116. package/build-module/components/dataform-controls/toggle.mjs.map +2 -2
  117. package/build-module/components/dataform-controls/utils/relative-date-control.mjs +5 -2
  118. package/build-module/components/dataform-controls/utils/relative-date-control.mjs.map +2 -2
  119. package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs +27 -0
  120. package/build-module/components/dataform-controls/utils/use-disabled-date-matchers.mjs.map +7 -0
  121. package/build-module/components/dataform-controls/utils/validated-input.mjs +2 -0
  122. package/build-module/components/dataform-controls/utils/validated-input.mjs.map +2 -2
  123. package/build-module/components/dataform-controls/utils/validated-number.mjs +3 -1
  124. package/build-module/components/dataform-controls/utils/validated-number.mjs.map +2 -2
  125. package/build-module/components/dataform-layouts/card/index.mjs +132 -133
  126. package/build-module/components/dataform-layouts/card/index.mjs.map +2 -2
  127. package/build-module/components/dataform-layouts/panel/summary-button.mjs +0 -1
  128. package/build-module/components/dataform-layouts/panel/summary-button.mjs.map +2 -2
  129. package/build-module/components/dataviews-bulk-actions/index.mjs +28 -5
  130. package/build-module/components/dataviews-bulk-actions/index.mjs.map +2 -2
  131. package/build-module/components/dataviews-context/index.mjs +2 -2
  132. package/build-module/components/dataviews-context/index.mjs.map +2 -2
  133. package/build-module/components/dataviews-filters/input-widget.mjs +4 -0
  134. package/build-module/components/dataviews-filters/input-widget.mjs.map +2 -2
  135. package/build-module/components/dataviews-footer/index.mjs +2 -3
  136. package/build-module/components/dataviews-footer/index.mjs.map +2 -2
  137. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs +387 -250
  138. package/build-module/components/dataviews-layouts/grid/composite-grid.mjs.map +2 -2
  139. package/build-module/components/dataviews-layouts/picker-grid/index.mjs +64 -31
  140. package/build-module/components/dataviews-layouts/picker-grid/index.mjs.map +2 -2
  141. package/build-module/components/dataviews-layouts/picker-table/index.mjs +45 -30
  142. package/build-module/components/dataviews-layouts/picker-table/index.mjs.map +2 -2
  143. package/build-module/components/dataviews-layouts/table/index.mjs +0 -1
  144. package/build-module/components/dataviews-layouts/table/index.mjs.map +2 -2
  145. package/build-module/components/dataviews-layouts/utils/use-infinite-scroll.mjs +26 -0
  146. package/build-module/components/dataviews-layouts/utils/use-infinite-scroll.mjs.map +7 -0
  147. package/build-module/components/dataviews-pagination/index.mjs +1 -0
  148. package/build-module/components/dataviews-pagination/index.mjs.map +2 -2
  149. package/build-module/components/dataviews-picker-footer/index.mjs +23 -4
  150. package/build-module/components/dataviews-picker-footer/index.mjs.map +2 -2
  151. package/build-module/components/dataviews-search/index.mjs +2 -1
  152. package/build-module/components/dataviews-search/index.mjs.map +2 -2
  153. package/build-module/components/dataviews-selection-checkbox/index.mjs +3 -2
  154. package/build-module/components/dataviews-selection-checkbox/index.mjs.map +2 -2
  155. package/build-module/components/dataviews-view-config/index.mjs +0 -2
  156. package/build-module/components/dataviews-view-config/index.mjs.map +2 -2
  157. package/build-module/components/dataviews-view-config/infinite-scroll-toggle.mjs +0 -3
  158. package/build-module/components/dataviews-view-config/infinite-scroll-toggle.mjs.map +2 -2
  159. package/build-module/dataviews/index.mjs +55 -47
  160. package/build-module/dataviews/index.mjs.map +2 -2
  161. package/build-module/dataviews-picker/index.mjs +49 -35
  162. package/build-module/dataviews-picker/index.mjs.map +2 -2
  163. package/build-module/field-types/date.mjs +4 -1
  164. package/build-module/field-types/date.mjs.map +2 -2
  165. package/build-module/field-types/datetime.mjs +4 -1
  166. package/build-module/field-types/datetime.mjs.map +2 -2
  167. package/build-module/field-types/index.mjs +1 -0
  168. package/build-module/field-types/index.mjs.map +2 -2
  169. package/build-module/field-types/utils/get-is-valid.mjs +29 -24
  170. package/build-module/field-types/utils/get-is-valid.mjs.map +2 -2
  171. package/build-module/field-types/utils/is-valid-date-boundary.mjs +38 -0
  172. package/build-module/field-types/utils/is-valid-date-boundary.mjs.map +7 -0
  173. package/build-module/hooks/index.mjs +7 -1
  174. package/build-module/hooks/index.mjs.map +2 -2
  175. package/build-module/hooks/use-data.mjs +147 -10
  176. package/build-module/hooks/use-data.mjs.map +2 -2
  177. package/build-module/hooks/use-infinite-scroll.mjs +188 -0
  178. package/build-module/hooks/use-infinite-scroll.mjs.map +7 -0
  179. package/build-module/hooks/use-selected-items.mjs +36 -0
  180. package/build-module/hooks/use-selected-items.mjs.map +7 -0
  181. package/build-module/utils/filter-sort-and-paginate.mjs +5 -1
  182. package/build-module/utils/filter-sort-and-paginate.mjs.map +2 -2
  183. package/build-module/utils/get-footer-message.mjs +8 -8
  184. package/build-module/utils/get-footer-message.mjs.map +2 -2
  185. package/build-style/style-rtl.css +75 -52
  186. package/build-style/style.css +75 -52
  187. package/build-types/components/dataform-controls/array.d.ts.map +1 -1
  188. package/build-types/components/dataform-controls/checkbox.d.ts.map +1 -1
  189. package/build-types/components/dataform-controls/color.d.ts.map +1 -1
  190. package/build-types/components/dataform-controls/date.d.ts.map +1 -1
  191. package/build-types/components/dataform-controls/datetime.d.ts +1 -1
  192. package/build-types/components/dataform-controls/datetime.d.ts.map +1 -1
  193. package/build-types/components/dataform-controls/password.d.ts.map +1 -1
  194. package/build-types/components/dataform-controls/radio.d.ts.map +1 -1
  195. package/build-types/components/dataform-controls/select.d.ts.map +1 -1
  196. package/build-types/components/dataform-controls/textarea.d.ts.map +1 -1
  197. package/build-types/components/dataform-controls/toggle-group.d.ts.map +1 -1
  198. package/build-types/components/dataform-controls/toggle.d.ts.map +1 -1
  199. package/build-types/components/dataform-controls/utils/relative-date-control.d.ts.map +1 -1
  200. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts +16 -0
  201. package/build-types/components/dataform-controls/utils/use-disabled-date-matchers.d.ts.map +1 -0
  202. package/build-types/components/dataform-controls/utils/validated-input.d.ts.map +1 -1
  203. package/build-types/components/dataform-controls/utils/validated-number.d.ts.map +1 -1
  204. package/build-types/components/dataform-layouts/card/index.d.ts.map +1 -1
  205. package/build-types/components/dataform-layouts/panel/summary-button.d.ts.map +1 -1
  206. package/build-types/components/dataviews-bulk-actions/index.d.ts +2 -1
  207. package/build-types/components/dataviews-bulk-actions/index.d.ts.map +1 -1
  208. package/build-types/components/dataviews-context/index.d.ts +3 -3
  209. package/build-types/components/dataviews-context/index.d.ts.map +1 -1
  210. package/build-types/components/dataviews-filters/input-widget.d.ts.map +1 -1
  211. package/build-types/components/dataviews-footer/index.d.ts.map +1 -1
  212. package/build-types/components/dataviews-layouts/grid/composite-grid.d.ts.map +1 -1
  213. package/build-types/components/dataviews-layouts/index.d.ts +6 -6
  214. package/build-types/components/dataviews-layouts/picker-grid/index.d.ts.map +1 -1
  215. package/build-types/components/dataviews-layouts/picker-table/index.d.ts.map +1 -1
  216. package/build-types/components/dataviews-layouts/table/index.d.ts.map +1 -1
  217. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts +22 -0
  218. package/build-types/components/dataviews-layouts/utils/use-infinite-scroll.d.ts.map +1 -0
  219. package/build-types/components/dataviews-pagination/index.d.ts.map +1 -1
  220. package/build-types/components/dataviews-picker-footer/index.d.ts.map +1 -1
  221. package/build-types/components/dataviews-search/index.d.ts +1 -1
  222. package/build-types/components/dataviews-search/index.d.ts.map +1 -1
  223. package/build-types/components/dataviews-selection-checkbox/index.d.ts.map +1 -1
  224. package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
  225. package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts +1 -1
  226. package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts.map +1 -1
  227. package/build-types/constants.d.ts +2 -2
  228. package/build-types/dataform/stories/index.story.d.ts +11 -1
  229. package/build-types/dataform/stories/index.story.d.ts.map +1 -1
  230. package/build-types/dataform/stories/layout-regular.d.ts +2 -1
  231. package/build-types/dataform/stories/layout-regular.d.ts.map +1 -1
  232. package/build-types/dataform/stories/validation.d.ts.map +1 -1
  233. package/build-types/dataviews/index.d.ts +1 -2
  234. package/build-types/dataviews/index.d.ts.map +1 -1
  235. package/build-types/dataviews/stories/fixtures.d.ts.map +1 -1
  236. package/build-types/dataviews/stories/free-composition.d.ts.map +1 -1
  237. package/build-types/dataviews/stories/index.story.d.ts +11 -0
  238. package/build-types/dataviews/stories/index.story.d.ts.map +1 -1
  239. package/build-types/dataviews/stories/infinite-scroll.d.ts.map +1 -1
  240. package/build-types/dataviews/stories/with-card.d.ts.map +1 -1
  241. package/build-types/dataviews-picker/index.d.ts +3 -3
  242. package/build-types/dataviews-picker/index.d.ts.map +1 -1
  243. package/build-types/dataviews-picker/stories/fixtures.d.ts.map +1 -1
  244. package/build-types/dataviews-picker/stories/index.story.d.ts.map +1 -1
  245. package/build-types/field-types/array.d.ts +1 -1
  246. package/build-types/field-types/array.d.ts.map +1 -1
  247. package/build-types/field-types/boolean.d.ts +1 -1
  248. package/build-types/field-types/boolean.d.ts.map +1 -1
  249. package/build-types/field-types/color.d.ts +1 -1
  250. package/build-types/field-types/color.d.ts.map +1 -1
  251. package/build-types/field-types/date.d.ts +3 -0
  252. package/build-types/field-types/date.d.ts.map +1 -1
  253. package/build-types/field-types/datetime.d.ts +3 -0
  254. package/build-types/field-types/datetime.d.ts.map +1 -1
  255. package/build-types/field-types/email.d.ts +1 -1
  256. package/build-types/field-types/email.d.ts.map +1 -1
  257. package/build-types/field-types/index.d.ts.map +1 -1
  258. package/build-types/field-types/integer.d.ts +1 -1
  259. package/build-types/field-types/integer.d.ts.map +1 -1
  260. package/build-types/field-types/number.d.ts +1 -1
  261. package/build-types/field-types/number.d.ts.map +1 -1
  262. package/build-types/field-types/stories/index.story.d.ts +37 -15
  263. package/build-types/field-types/stories/index.story.d.ts.map +1 -1
  264. package/build-types/field-types/utils/get-is-valid.d.ts.map +1 -1
  265. package/build-types/field-types/utils/is-valid-date-boundary.d.ts +7 -0
  266. package/build-types/field-types/utils/is-valid-date-boundary.d.ts.map +1 -0
  267. package/build-types/hooks/index.d.ts +3 -0
  268. package/build-types/hooks/index.d.ts.map +1 -1
  269. package/build-types/hooks/test/use-data.d.ts +2 -0
  270. package/build-types/hooks/test/use-data.d.ts.map +1 -0
  271. package/build-types/hooks/use-data.d.ts +41 -3
  272. package/build-types/hooks/use-data.d.ts.map +1 -1
  273. package/build-types/hooks/use-infinite-scroll.d.ts +21 -0
  274. package/build-types/hooks/use-infinite-scroll.d.ts.map +1 -0
  275. package/build-types/hooks/use-selected-items.d.ts +19 -0
  276. package/build-types/hooks/use-selected-items.d.ts.map +1 -0
  277. package/build-types/types/dataviews.d.ts +15 -1
  278. package/build-types/types/dataviews.d.ts.map +1 -1
  279. package/build-types/types/field-api.d.ts +39 -13
  280. package/build-types/types/field-api.d.ts.map +1 -1
  281. package/build-types/utils/filter-sort-and-paginate.d.ts.map +1 -1
  282. package/build-types/utils/get-footer-message.d.ts +3 -2
  283. package/build-types/utils/get-footer-message.d.ts.map +1 -1
  284. package/build-wp/index.js +3264 -2713
  285. package/package.json +19 -19
  286. package/src/components/dataform-controls/array.tsx +2 -0
  287. package/src/components/dataform-controls/checkbox.tsx +2 -0
  288. package/src/components/dataform-controls/color.tsx +7 -0
  289. package/src/components/dataform-controls/date.tsx +30 -4
  290. package/src/components/dataform-controls/datetime.tsx +36 -11
  291. package/src/components/dataform-controls/password.tsx +3 -0
  292. package/src/components/dataform-controls/radio.tsx +2 -0
  293. package/src/components/dataform-controls/select.tsx +2 -0
  294. package/src/components/dataform-controls/textarea.tsx +2 -0
  295. package/src/components/dataform-controls/toggle-group.tsx +2 -0
  296. package/src/components/dataform-controls/toggle.tsx +2 -0
  297. package/src/components/dataform-controls/utils/relative-date-control.tsx +3 -0
  298. package/src/components/dataform-controls/utils/use-disabled-date-matchers.ts +48 -0
  299. package/src/components/dataform-controls/utils/validated-input.tsx +2 -0
  300. package/src/components/dataform-controls/utils/validated-number.tsx +2 -0
  301. package/src/components/dataform-layouts/card/index.tsx +171 -146
  302. package/src/components/dataform-layouts/card/style.scss +8 -5
  303. package/src/components/dataform-layouts/panel/style.scss +4 -5
  304. package/src/components/dataform-layouts/panel/summary-button.tsx +0 -1
  305. package/src/components/dataviews-bulk-actions/index.tsx +28 -1
  306. package/src/components/dataviews-context/index.ts +4 -4
  307. package/src/components/dataviews-filters/input-widget.tsx +4 -0
  308. package/src/components/dataviews-filters/style.scss +2 -2
  309. package/src/components/dataviews-footer/index.tsx +1 -6
  310. package/src/components/dataviews-layouts/activity/style.scss +3 -3
  311. package/src/components/dataviews-layouts/grid/composite-grid.tsx +433 -284
  312. package/src/components/dataviews-layouts/grid/style.scss +5 -1
  313. package/src/components/dataviews-layouts/list/style.scss +1 -1
  314. package/src/components/dataviews-layouts/picker-grid/index.tsx +49 -15
  315. package/src/components/dataviews-layouts/picker-grid/style.scss +1 -1
  316. package/src/components/dataviews-layouts/picker-table/index.tsx +45 -23
  317. package/src/components/dataviews-layouts/picker-table/style.scss +1 -1
  318. package/src/components/dataviews-layouts/table/index.tsx +0 -2
  319. package/src/components/dataviews-layouts/utils/use-infinite-scroll.ts +64 -0
  320. package/src/components/dataviews-pagination/index.tsx +1 -0
  321. package/src/components/dataviews-picker-footer/index.tsx +21 -1
  322. package/src/components/dataviews-search/index.tsx +2 -1
  323. package/src/components/dataviews-selection-checkbox/index.tsx +4 -2
  324. package/src/components/dataviews-view-config/index.tsx +0 -2
  325. package/src/components/dataviews-view-config/infinite-scroll-toggle.tsx +0 -5
  326. package/src/dataform/stories/content.story.tsx +1 -1
  327. package/src/dataform/stories/data-adapter.tsx +6 -6
  328. package/src/dataform/stories/index.story.tsx +7 -0
  329. package/src/dataform/stories/layout-card.tsx +5 -5
  330. package/src/dataform/stories/layout-details.tsx +5 -5
  331. package/src/dataform/stories/layout-panel.tsx +9 -9
  332. package/src/dataform/stories/layout-regular.tsx +31 -10
  333. package/src/dataform/stories/layout-row.tsx +9 -9
  334. package/src/dataform/stories/validation.tsx +25 -10
  335. package/src/dataviews/index.tsx +68 -59
  336. package/src/dataviews/stories/empty.tsx +4 -4
  337. package/src/dataviews/stories/fixtures.tsx +288 -0
  338. package/src/dataviews/stories/free-composition.tsx +14 -13
  339. package/src/dataviews/stories/index.story.tsx +19 -2
  340. package/src/dataviews/stories/infinite-scroll.tsx +16 -96
  341. package/src/dataviews/stories/layout-custom.tsx +1 -1
  342. package/src/dataviews/stories/layout-grid.tsx +1 -1
  343. package/src/dataviews/stories/layout-list.tsx +1 -1
  344. package/src/dataviews/stories/layout-table.tsx +1 -1
  345. package/src/dataviews/stories/minimal-ui.tsx +1 -1
  346. package/src/dataviews/stories/with-card.tsx +30 -23
  347. package/src/dataviews/style.scss +6 -8
  348. package/src/dataviews/test/dataviews.tsx +94 -15
  349. package/src/dataviews-picker/index.tsx +57 -41
  350. package/src/dataviews-picker/stories/fixtures.tsx +270 -0
  351. package/src/dataviews-picker/stories/index.story.tsx +62 -133
  352. package/src/dataviews-picker/test/dataviews-picker.tsx +79 -2
  353. package/src/field-types/date.tsx +3 -0
  354. package/src/field-types/datetime.tsx +3 -0
  355. package/src/field-types/index.tsx +4 -0
  356. package/src/field-types/stories/index.story.tsx +79 -6
  357. package/src/field-types/test/normalize-fields.ts +44 -0
  358. package/src/field-types/utils/get-is-valid.ts +44 -31
  359. package/src/field-types/utils/is-valid-date-boundary.ts +80 -0
  360. package/src/hooks/index.ts +3 -0
  361. package/src/hooks/test/use-data.ts +791 -0
  362. package/src/hooks/test/use-form-validity.ts +479 -0
  363. package/src/hooks/use-data.ts +288 -21
  364. package/src/hooks/use-infinite-scroll.ts +304 -0
  365. package/src/hooks/use-selected-items.ts +72 -0
  366. package/src/types/dataviews.ts +17 -1
  367. package/src/types/field-api.ts +43 -12
  368. package/src/utils/filter-sort-and-paginate.ts +13 -1
  369. package/src/utils/get-footer-message.ts +12 -9
  370. package/src/utils/test/filter-sort-and-paginate.js +78 -54
@@ -0,0 +1,791 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { renderHook, act } from '@testing-library/react';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import useData from '../use-data';
10
+ import type { View } from '../../types';
11
+
12
+ type TestItem = { id: number; name?: string };
13
+
14
+ const getItemId = ( item: TestItem ) => String( item.id );
15
+ const defaultPaginationInfo = { totalItems: 100, totalPages: 10 };
16
+
17
+ describe( 'useData', () => {
18
+ describe( 'when infinite scroll is disabled', () => {
19
+ it( 'returns provided data unchanged', () => {
20
+ const data: TestItem[] = [
21
+ { id: 1, name: 'Item 1' },
22
+ { id: 2, name: 'Item 2' },
23
+ ];
24
+ const view = {
25
+ type: 'table',
26
+ infiniteScrollEnabled: false,
27
+ } as View;
28
+
29
+ const { result } = renderHook( () =>
30
+ useData( {
31
+ view,
32
+ data,
33
+ getItemId,
34
+ paginationInfo: defaultPaginationInfo,
35
+ } )
36
+ );
37
+
38
+ expect( result.current.data ).toEqual( data );
39
+ expect( result.current.setVisibleEntries ).toBeUndefined();
40
+ } );
41
+
42
+ it( 'returns empty array when data is empty', () => {
43
+ const data: TestItem[] = [];
44
+ const view = {
45
+ type: 'table',
46
+ infiniteScrollEnabled: false,
47
+ } as View;
48
+
49
+ const { result } = renderHook( () =>
50
+ useData( {
51
+ view,
52
+ data,
53
+ getItemId,
54
+ paginationInfo: defaultPaginationInfo,
55
+ } )
56
+ );
57
+
58
+ expect( result.current.data ).toEqual( [] );
59
+ } );
60
+ } );
61
+
62
+ describe( 'when infinite scroll is enabled', () => {
63
+ describe( 'initial load', () => {
64
+ it( 'assigns positions starting from 1 by default', () => {
65
+ const data: TestItem[] = [
66
+ { id: 1, name: 'Item 1' },
67
+ { id: 2, name: 'Item 2' },
68
+ ];
69
+ const view = {
70
+ type: 'table',
71
+ infiniteScrollEnabled: true,
72
+ } as View;
73
+
74
+ const { result } = renderHook( () =>
75
+ useData( {
76
+ view,
77
+ data,
78
+ getItemId,
79
+ paginationInfo: defaultPaginationInfo,
80
+ } )
81
+ );
82
+
83
+ expect( result.current.data ).toHaveLength( 2 );
84
+ expect(
85
+ (
86
+ result.current.data[ 0 ] as TestItem & {
87
+ position: number;
88
+ }
89
+ ).position
90
+ ).toBe( 1 );
91
+ expect(
92
+ (
93
+ result.current.data[ 1 ] as TestItem & {
94
+ position: number;
95
+ }
96
+ ).position
97
+ ).toBe( 2 );
98
+ } );
99
+
100
+ it( 'assigns positions starting from view.startPosition when provided', () => {
101
+ const data: TestItem[] = [
102
+ { id: 1, name: 'Item 1' },
103
+ { id: 2, name: 'Item 2' },
104
+ ];
105
+ const view = {
106
+ type: 'table',
107
+ infiniteScrollEnabled: true,
108
+ startPosition: 5,
109
+ } as View;
110
+
111
+ const { result } = renderHook( () =>
112
+ useData( {
113
+ view,
114
+ data,
115
+ getItemId,
116
+ paginationInfo: defaultPaginationInfo,
117
+ } )
118
+ );
119
+
120
+ expect(
121
+ (
122
+ result.current.data[ 1 ] as TestItem & {
123
+ position: number;
124
+ }
125
+ ).position
126
+ ).toBe( 6 );
127
+ } );
128
+
129
+ it( 'returns setVisibleEntries callback', () => {
130
+ const data: TestItem[] = [ { id: 1 } ];
131
+ const view = {
132
+ type: 'table',
133
+ infiniteScrollEnabled: true,
134
+ } as View;
135
+
136
+ const { result } = renderHook( () =>
137
+ useData( {
138
+ view,
139
+ data,
140
+ getItemId,
141
+ paginationInfo: defaultPaginationInfo,
142
+ } )
143
+ );
144
+
145
+ expect( result.current.setVisibleEntries ).toBeDefined();
146
+ expect( typeof result.current.setVisibleEntries ).toBe(
147
+ 'function'
148
+ );
149
+ } );
150
+ } );
151
+
152
+ describe( 'loading more data when scrolling down', () => {
153
+ it( 'appends new items with positions after existing items', () => {
154
+ const initialData: TestItem[] = [
155
+ { id: 1, name: 'Item 1' },
156
+ { id: 2, name: 'Item 2' },
157
+ ];
158
+ const initialView = {
159
+ type: 'table',
160
+ infiniteScrollEnabled: true,
161
+ startPosition: 1,
162
+ } as View;
163
+
164
+ const { result, rerender } = renderHook(
165
+ ( { view, data } ) =>
166
+ useData( {
167
+ view,
168
+ data,
169
+ getItemId,
170
+ paginationInfo: defaultPaginationInfo,
171
+ } ),
172
+ { initialProps: { view: initialView, data: initialData } }
173
+ );
174
+
175
+ expect( result.current.data ).toHaveLength( 2 );
176
+
177
+ // Simulate scroll down - new page of data
178
+ const newData: TestItem[] = [
179
+ { id: 3, name: 'Item 3' },
180
+ { id: 4, name: 'Item 4' },
181
+ ];
182
+ const newView = {
183
+ ...initialView,
184
+ startPosition: 3,
185
+ } as View;
186
+
187
+ rerender( { view: newView, data: newData } );
188
+
189
+ // Should have all 4 items
190
+ expect( result.current.data ).toHaveLength( 4 );
191
+ // Items should be sorted by position
192
+ const positions = result.current.data.map(
193
+ ( d: TestItem & { position?: number } ) =>
194
+ ( d as TestItem & { position: number } ).position
195
+ );
196
+ expect( positions ).toEqual( [ 1, 2, 3, 4 ] );
197
+ } );
198
+
199
+ it( 'preserves positions for existing items', () => {
200
+ const initialData: TestItem[] = [ { id: 1 }, { id: 2 } ];
201
+ const initialView = {
202
+ type: 'table',
203
+ infiniteScrollEnabled: true,
204
+ startPosition: 1,
205
+ } as View;
206
+
207
+ const { result, rerender } = renderHook(
208
+ ( { view, data } ) =>
209
+ useData( {
210
+ view,
211
+ data,
212
+ getItemId,
213
+ paginationInfo: defaultPaginationInfo,
214
+ } ),
215
+ { initialProps: { view: initialView, data: initialData } }
216
+ );
217
+
218
+ // Scroll down with overlapping data
219
+ const newData: TestItem[] = [
220
+ { id: 2 }, // Already exists
221
+ { id: 3 }, // New
222
+ ];
223
+ const newView = { ...initialView, startPosition: 2 } as View;
224
+
225
+ rerender( { view: newView, data: newData } );
226
+
227
+ // Item 2 should keep its original position (2)
228
+ const item2 = result.current.data.find(
229
+ ( d: TestItem & { position?: number } ) => d.id === 2
230
+ ) as TestItem & { position: number };
231
+ expect( item2.position ).toBe( 2 );
232
+
233
+ // Item 3 should get position 3
234
+ const item3 = result.current.data.find(
235
+ ( d: TestItem & { position?: number } ) => d.id === 3
236
+ ) as TestItem & { position: number };
237
+ expect( item3.position ).toBe( 3 );
238
+ } );
239
+ } );
240
+
241
+ describe( 'loading more data when scrolling up', () => {
242
+ it( 'prepends new items with positions before existing items', () => {
243
+ // Start with items at positions 5, 6
244
+ const initialData: TestItem[] = [
245
+ { id: 5, name: 'Item 5' },
246
+ { id: 6, name: 'Item 6' },
247
+ ];
248
+ const initialView = {
249
+ type: 'table',
250
+ infiniteScrollEnabled: true,
251
+ startPosition: 5,
252
+ } as View;
253
+
254
+ const { result, rerender } = renderHook(
255
+ ( { view, data } ) =>
256
+ useData( {
257
+ view,
258
+ data,
259
+ getItemId,
260
+ paginationInfo: defaultPaginationInfo,
261
+ } ),
262
+ { initialProps: { view: initialView, data: initialData } }
263
+ );
264
+
265
+ expect( result.current.data ).toHaveLength( 2 );
266
+
267
+ // Simulate scroll up - load earlier data
268
+ const newData: TestItem[] = [
269
+ { id: 3, name: 'Item 3' },
270
+ { id: 4, name: 'Item 4' },
271
+ ];
272
+ const newView = {
273
+ ...initialView,
274
+ startPosition: 3, // Scrolling up
275
+ } as View;
276
+
277
+ rerender( { view: newView, data: newData } );
278
+
279
+ // Should have all 4 items
280
+ expect( result.current.data ).toHaveLength( 4 );
281
+ // Items should be sorted by position (ascending)
282
+ const ids = result.current.data.map(
283
+ ( d: TestItem & { position?: number } ) => d.id
284
+ );
285
+ expect( ids ).toEqual( [ 3, 4, 5, 6 ] );
286
+ } );
287
+ } );
288
+
289
+ describe( 'deduplication', () => {
290
+ it( 'removes duplicates when new data overlaps with existing data', () => {
291
+ const initialData: TestItem[] = [
292
+ { id: 1 },
293
+ { id: 2 },
294
+ { id: 3 },
295
+ ];
296
+ const initialView = {
297
+ type: 'table',
298
+ infiniteScrollEnabled: true,
299
+ startPosition: 1,
300
+ } as View;
301
+
302
+ const { result, rerender } = renderHook(
303
+ ( { view, data } ) =>
304
+ useData( {
305
+ view,
306
+ data,
307
+ getItemId,
308
+ paginationInfo: defaultPaginationInfo,
309
+ } ),
310
+ { initialProps: { view: initialView, data: initialData } }
311
+ );
312
+
313
+ // New data with overlapping items
314
+ const newData: TestItem[] = [ { id: 2 }, { id: 3 }, { id: 4 } ];
315
+ const newView = { ...initialView, startPosition: 2 } as View;
316
+
317
+ rerender( { view: newView, data: newData } );
318
+
319
+ // Should not have duplicate IDs
320
+ const ids = result.current.data.map(
321
+ ( d: TestItem & { position?: number } ) => d.id
322
+ );
323
+ const uniqueIds = [ ...new Set( ids ) ];
324
+ expect( ids ).toEqual( uniqueIds );
325
+ } );
326
+ } );
327
+
328
+ describe( 'buffer and unloading', () => {
329
+ it( 'keeps selected items even when outside visible buffer', () => {
330
+ const initialData: TestItem[] = Array.from(
331
+ { length: 30 },
332
+ ( _, i ) => ( { id: i + 1 } )
333
+ );
334
+ const initialView = {
335
+ type: 'table',
336
+ infiniteScrollEnabled: true,
337
+ startPosition: 1,
338
+ } as View;
339
+
340
+ const { result, rerender } = renderHook(
341
+ ( { view, data, selection } ) =>
342
+ useData( {
343
+ view,
344
+ data,
345
+ getItemId,
346
+ selection,
347
+ paginationInfo: defaultPaginationInfo,
348
+ } ),
349
+ {
350
+ initialProps: {
351
+ view: initialView,
352
+ data: initialData,
353
+ selection: [ '5' ],
354
+ },
355
+ }
356
+ );
357
+
358
+ act( () => {
359
+ result.current.setVisibleEntries?.( [ 50, 51 ] );
360
+ } );
361
+
362
+ const newData: TestItem[] = Array.from(
363
+ { length: 10 },
364
+ ( _, i ) => ( { id: 31 + i } )
365
+ );
366
+ const newView = { ...initialView, startPosition: 31 } as View;
367
+
368
+ rerender( {
369
+ view: newView,
370
+ data: newData,
371
+ selection: [ '5' ],
372
+ } );
373
+
374
+ expect(
375
+ result.current.data.some(
376
+ ( item: TestItem & { position?: number } ) =>
377
+ item.id === 5
378
+ )
379
+ ).toBe( true );
380
+ } );
381
+
382
+ it( 'keeps items within buffer range of visible entries', () => {
383
+ // Create a large dataset
384
+ const initialData: TestItem[] = Array.from(
385
+ { length: 30 },
386
+ ( _, i ) => ( { id: i + 1 } )
387
+ );
388
+ const initialView = {
389
+ type: 'table',
390
+ infiniteScrollEnabled: true,
391
+ startPosition: 1,
392
+ } as View;
393
+
394
+ const { result, rerender } = renderHook(
395
+ ( { view, data } ) =>
396
+ useData( {
397
+ view,
398
+ data,
399
+ getItemId,
400
+ paginationInfo: defaultPaginationInfo,
401
+ } ),
402
+ { initialProps: { view: initialView, data: initialData } }
403
+ );
404
+
405
+ expect( result.current.data ).toHaveLength( 30 );
406
+
407
+ // Set visible entries to middle of the list
408
+ act( () => {
409
+ result.current.setVisibleEntries?.( [ 15, 16, 17, 18 ] );
410
+ } );
411
+
412
+ // Scroll down with new data to trigger buffer logic
413
+ const newData: TestItem[] = Array.from(
414
+ { length: 10 },
415
+ ( _, i ) => ( { id: 31 + i } )
416
+ );
417
+ const newView = { ...initialView, startPosition: 31 } as View;
418
+
419
+ rerender( { view: newView, data: newData } );
420
+
421
+ // Items far above visible range should be trimmed (buffer of 20)
422
+ // Visible min is 15, so items below position (15 - 20 = -5) should be removed
423
+ // Since all items have positive positions, none should be removed in this case
424
+ // when scrolling down
425
+ const positions = result.current.data.map(
426
+ ( d: TestItem & { position?: number } ) =>
427
+ ( d as TestItem & { position: number } ).position
428
+ );
429
+ const minPosition = Math.min( ...positions );
430
+
431
+ // When scrolling down, items above visible range minus buffer should be trimmed
432
+ // visibleMin - buffer = 15 - 20 = -5, so all items >= -5 are kept
433
+ expect( minPosition ).toBeGreaterThanOrEqual( -5 );
434
+ } );
435
+
436
+ it( 'trims items from the end when scrolling up', () => {
437
+ // Start with items at high positions
438
+ const initialData: TestItem[] = Array.from(
439
+ { length: 30 },
440
+ ( _, i ) => ( { id: i + 50 } )
441
+ );
442
+ const initialView = {
443
+ type: 'table',
444
+ infiniteScrollEnabled: true,
445
+ startPosition: 50,
446
+ } as View;
447
+
448
+ const { result, rerender } = renderHook(
449
+ ( { view, data } ) =>
450
+ useData( {
451
+ view,
452
+ data,
453
+ getItemId,
454
+ paginationInfo: defaultPaginationInfo,
455
+ } ),
456
+ { initialProps: { view: initialView, data: initialData } }
457
+ );
458
+
459
+ // Set visible entries
460
+ act( () => {
461
+ result.current.setVisibleEntries?.( [ 55, 56, 57, 58 ] );
462
+ } );
463
+
464
+ // Scroll up with new data
465
+ const newData: TestItem[] = Array.from(
466
+ { length: 10 },
467
+ ( _, i ) => ( {
468
+ id: i + 40,
469
+ } )
470
+ );
471
+ const newView = { ...initialView, startPosition: 40 } as View;
472
+
473
+ rerender( { view: newView, data: newData } );
474
+
475
+ // When scrolling up, items below visible range + buffer should be trimmed
476
+ // visibleMax + buffer = 58 + 20 = 78
477
+ const positions = result.current.data.map(
478
+ ( d: TestItem & { position?: number } ) =>
479
+ ( d as TestItem & { position: number } ).position
480
+ );
481
+ const maxPosition = Math.max( ...positions );
482
+
483
+ expect( maxPosition ).toBeLessThanOrEqual( 78 );
484
+ } );
485
+ } );
486
+
487
+ describe( 'view changes', () => {
488
+ it( 'resets data when search changes', () => {
489
+ const initialData: TestItem[] = [ { id: 1 }, { id: 2 } ];
490
+ const initialView = {
491
+ type: 'table',
492
+ infiniteScrollEnabled: true,
493
+ startPosition: 1,
494
+ search: '',
495
+ } as View;
496
+
497
+ const { result, rerender } = renderHook(
498
+ ( { view, data } ) =>
499
+ useData( {
500
+ view,
501
+ data,
502
+ getItemId,
503
+ paginationInfo: defaultPaginationInfo,
504
+ } ),
505
+ { initialProps: { view: initialView, data: initialData } }
506
+ );
507
+
508
+ expect( result.current.data ).toHaveLength( 2 );
509
+
510
+ // Change search - data should be replaced, not appended
511
+ const newData: TestItem[] = [ { id: 3 } ];
512
+ const newView = { ...initialView, search: 'test' } as View;
513
+
514
+ rerender( { view: newView, data: newData } );
515
+
516
+ // Data should be reset to just the new search results
517
+ expect( result.current.data ).toHaveLength( 1 );
518
+ expect( result.current.data[ 0 ].id ).toBe( 3 );
519
+ } );
520
+
521
+ it( 'resets data when filters change', () => {
522
+ const initialData: TestItem[] = [ { id: 1 }, { id: 2 } ];
523
+ const initialView = {
524
+ type: 'table',
525
+ infiniteScrollEnabled: true,
526
+ startPosition: 1,
527
+ filters: [],
528
+ } as View;
529
+
530
+ const { result, rerender } = renderHook(
531
+ ( { view, data } ) =>
532
+ useData( {
533
+ view,
534
+ data,
535
+ getItemId,
536
+ paginationInfo: defaultPaginationInfo,
537
+ } ),
538
+ { initialProps: { view: initialView, data: initialData } }
539
+ );
540
+
541
+ expect( result.current.data ).toHaveLength( 2 );
542
+
543
+ // Change filters - data should be replaced
544
+ const newData: TestItem[] = [ { id: 5 } ];
545
+ const newView = {
546
+ ...initialView,
547
+ filters: [
548
+ { field: 'status', operator: 'is', value: 'published' },
549
+ ],
550
+ } as View;
551
+
552
+ rerender( { view: newView, data: newData } );
553
+
554
+ expect( result.current.data ).toHaveLength( 1 );
555
+ expect( result.current.data[ 0 ].id ).toBe( 5 );
556
+ } );
557
+
558
+ it( 'resets data when perPage changes', () => {
559
+ const initialData: TestItem[] = [ { id: 1 }, { id: 2 } ];
560
+ const initialView = {
561
+ type: 'table',
562
+ infiniteScrollEnabled: true,
563
+ startPosition: 1,
564
+ perPage: 10,
565
+ } as View;
566
+
567
+ const { result, rerender } = renderHook(
568
+ ( { view, data } ) =>
569
+ useData( {
570
+ view,
571
+ data,
572
+ getItemId,
573
+ paginationInfo: defaultPaginationInfo,
574
+ } ),
575
+ { initialProps: { view: initialView, data: initialData } }
576
+ );
577
+
578
+ expect( result.current.data ).toHaveLength( 2 );
579
+
580
+ // Change perPage - data should be replaced
581
+ const newData: TestItem[] = [
582
+ { id: 1 },
583
+ { id: 2 },
584
+ { id: 3 },
585
+ { id: 4 },
586
+ { id: 5 },
587
+ ];
588
+ const newView = { ...initialView, perPage: 25 } as View;
589
+
590
+ rerender( { view: newView, data: newData } );
591
+
592
+ expect( result.current.data ).toHaveLength( 5 );
593
+ } );
594
+
595
+ it( 'handles transition from infinite scroll disabled to enabled', () => {
596
+ const data: TestItem[] = [ { id: 1 }, { id: 2 } ];
597
+ const initialView = {
598
+ type: 'table',
599
+ infiniteScrollEnabled: false,
600
+ } as View;
601
+
602
+ const { result, rerender } = renderHook(
603
+ ( { view, passedData } ) =>
604
+ useData( {
605
+ view,
606
+ data: passedData,
607
+ getItemId,
608
+ paginationInfo: defaultPaginationInfo,
609
+ } ),
610
+ { initialProps: { view: initialView, passedData: data } }
611
+ );
612
+
613
+ expect( result.current.setVisibleEntries ).toBeUndefined();
614
+
615
+ // Enable infinite scroll
616
+ const newView = {
617
+ ...initialView,
618
+ infiniteScrollEnabled: true,
619
+ startPosition: 1,
620
+ } as View;
621
+
622
+ rerender( { view: newView, passedData: data } );
623
+
624
+ expect( result.current.setVisibleEntries ).toBeDefined();
625
+ expect( result.current.data ).toHaveLength( 2 );
626
+ } );
627
+
628
+ it( 'returns all data when clearing search after filtering', () => {
629
+ // This tests the scenario where:
630
+ // 1. User has full data list
631
+ // 2. User searches and gets fewer results
632
+ // 3. Visible entries are set for the filtered results
633
+ // 4. User clears search
634
+ // 5. Full data should be returned, not limited by stale visible entries
635
+ const fullData: TestItem[] = Array.from(
636
+ { length: 25 },
637
+ ( _, i ) => ( { id: i + 1 } )
638
+ );
639
+ const initialView = {
640
+ type: 'table',
641
+ infiniteScrollEnabled: true,
642
+ startPosition: 1,
643
+ search: '',
644
+ } as View;
645
+
646
+ const { result, rerender } = renderHook(
647
+ ( { view, data } ) =>
648
+ useData( {
649
+ view,
650
+ data,
651
+ getItemId,
652
+ paginationInfo: defaultPaginationInfo,
653
+ } ),
654
+ { initialProps: { view: initialView, data: fullData } }
655
+ );
656
+
657
+ // Simulate visible entries being set for the 6 items
658
+ act( () => {
659
+ result.current.setVisibleEntries?.( [ 1, 2, 3, 4, 5, 6 ] );
660
+ } );
661
+ const clearedView = { ...initialView, search: '' } as View;
662
+ rerender( { view: clearedView, data: fullData } );
663
+
664
+ // Should return all 25 items, not limited by stale visible entries
665
+ expect( result.current.data ).toHaveLength( 25 );
666
+ } );
667
+
668
+ it( 'returns all data when changing search term', () => {
669
+ const initialView = {
670
+ type: 'table',
671
+ infiniteScrollEnabled: true,
672
+ startPosition: 1,
673
+ search: 'foo',
674
+ } as View;
675
+ const fooResults: TestItem[] = [
676
+ { id: 1 },
677
+ { id: 2 },
678
+ { id: 3 },
679
+ ];
680
+
681
+ const { result, rerender } = renderHook(
682
+ ( { view, data } ) =>
683
+ useData( {
684
+ view,
685
+ data,
686
+ getItemId,
687
+ paginationInfo: defaultPaginationInfo,
688
+ } ),
689
+ { initialProps: { view: initialView, data: fooResults } }
690
+ );
691
+
692
+ expect( result.current.data ).toHaveLength( 3 );
693
+
694
+ // Set visible entries for the 3 items
695
+ act( () => {
696
+ result.current.setVisibleEntries?.( [ 1, 2, 3 ] );
697
+ } );
698
+
699
+ // Change to a different search with more results
700
+ const barResults: TestItem[] = Array.from(
701
+ { length: 15 },
702
+ ( _, i ) => ( { id: i + 10 } )
703
+ );
704
+ const barView = { ...initialView, search: 'bar' } as View;
705
+ rerender( { view: barView, data: barResults } );
706
+
707
+ // Should return all 15 items, not limited to 3
708
+ expect( result.current.data ).toHaveLength( 15 );
709
+ } );
710
+
711
+ it( 'handles single item', () => {
712
+ const data: TestItem[] = [ { id: 1 } ];
713
+ const view = {
714
+ type: 'table',
715
+ infiniteScrollEnabled: true,
716
+ startPosition: 1,
717
+ } as View;
718
+
719
+ const { result } = renderHook( () =>
720
+ useData( {
721
+ view,
722
+ data,
723
+ getItemId,
724
+ paginationInfo: defaultPaginationInfo,
725
+ } )
726
+ );
727
+
728
+ expect( result.current.data ).toHaveLength( 1 );
729
+ expect(
730
+ (
731
+ result.current.data[ 0 ] as TestItem & {
732
+ position: number;
733
+ }
734
+ ).position
735
+ ).toBe( 1 );
736
+ } );
737
+
738
+ it( 'maintains correct order after multiple scroll direction changes', () => {
739
+ const initialData: TestItem[] = [
740
+ { id: 5 },
741
+ { id: 6 },
742
+ { id: 7 },
743
+ ];
744
+ const initialView = {
745
+ type: 'table',
746
+ infiniteScrollEnabled: true,
747
+ startPosition: 5,
748
+ } as View;
749
+
750
+ const { result, rerender } = renderHook(
751
+ ( { view, data } ) =>
752
+ useData( {
753
+ view,
754
+ data,
755
+ getItemId,
756
+ paginationInfo: defaultPaginationInfo,
757
+ } ),
758
+ { initialProps: { view: initialView, data: initialData } }
759
+ );
760
+
761
+ // Scroll down
762
+ rerender( {
763
+ view: { ...initialView, startPosition: 8 } as View,
764
+ data: [ { id: 8 }, { id: 9 } ],
765
+ } );
766
+
767
+ // Scroll up
768
+ rerender( {
769
+ view: { ...initialView, startPosition: 3 } as View,
770
+ data: [ { id: 3 }, { id: 4 } ],
771
+ } );
772
+
773
+ // Scroll down again
774
+ rerender( {
775
+ view: { ...initialView, startPosition: 10 } as View,
776
+ data: [ { id: 10 } ],
777
+ } );
778
+
779
+ // All items should be in ascending order by position
780
+ const positions = result.current.data.map(
781
+ ( d: TestItem & { position?: number } ) =>
782
+ ( d as TestItem & { position: number } ).position
783
+ );
784
+ const sortedPositions = [ ...positions ].sort(
785
+ ( a, b ) => a - b
786
+ );
787
+ expect( positions ).toEqual( sortedPositions );
788
+ } );
789
+ } );
790
+ } );
791
+ } );