@glideappsfinal/glide-data-grid 6.0.9

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 (745) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc +68 -0
  3. package/API.md +1466 -0
  4. package/CHANGELOG.md +895 -0
  5. package/LICENSE +21 -0
  6. package/README.md +190 -0
  7. package/build.sh +21 -0
  8. package/dist/cjs/cells/boolean-cell.js +87 -0
  9. package/dist/cjs/cells/boolean-cell.js.map +1 -0
  10. package/dist/cjs/cells/bubble-cell.js +53 -0
  11. package/dist/cjs/cells/bubble-cell.js.map +1 -0
  12. package/dist/cjs/cells/cell-types.js +2 -0
  13. package/dist/cjs/cells/cell-types.js.map +1 -0
  14. package/dist/cjs/cells/drilldown-cell.js +171 -0
  15. package/dist/cjs/cells/drilldown-cell.js.map +1 -0
  16. package/dist/cjs/cells/image-cell.js +94 -0
  17. package/dist/cjs/cells/image-cell.js.map +1 -0
  18. package/dist/cjs/cells/index.js +29 -0
  19. package/dist/cjs/cells/index.js.map +1 -0
  20. package/dist/cjs/cells/loading-cell.js +41 -0
  21. package/dist/cjs/cells/loading-cell.js.map +1 -0
  22. package/dist/cjs/cells/markdown-cell.js +30 -0
  23. package/dist/cjs/cells/markdown-cell.js.map +1 -0
  24. package/dist/cjs/cells/marker-cell.js +80 -0
  25. package/dist/cjs/cells/marker-cell.js.map +1 -0
  26. package/dist/cjs/cells/new-row-cell.js +53 -0
  27. package/dist/cjs/cells/new-row-cell.js.map +1 -0
  28. package/dist/cjs/cells/number-cell.js +44 -0
  29. package/dist/cjs/cells/number-cell.js.map +1 -0
  30. package/dist/cjs/cells/protected-cell.js +35 -0
  31. package/dist/cjs/cells/protected-cell.js.map +1 -0
  32. package/dist/cjs/cells/row-id-cell.js +23 -0
  33. package/dist/cjs/cells/row-id-cell.js.map +1 -0
  34. package/dist/cjs/cells/text-cell.js +48 -0
  35. package/dist/cjs/cells/text-cell.js.map +1 -0
  36. package/dist/cjs/cells/uri-cell.js +104 -0
  37. package/dist/cjs/cells/uri-cell.js.map +1 -0
  38. package/dist/cjs/common/browser-detect.js +20 -0
  39. package/dist/cjs/common/browser-detect.js.map +1 -0
  40. package/dist/cjs/common/image-window-loader.js +98 -0
  41. package/dist/cjs/common/image-window-loader.js.map +1 -0
  42. package/dist/cjs/common/is-hotkey.js +76 -0
  43. package/dist/cjs/common/is-hotkey.js.map +1 -0
  44. package/dist/cjs/common/math.js +297 -0
  45. package/dist/cjs/common/math.js.map +1 -0
  46. package/dist/cjs/common/render-state-provider.js +70 -0
  47. package/dist/cjs/common/render-state-provider.js.map +1 -0
  48. package/dist/cjs/common/resize-detector.js +27 -0
  49. package/dist/cjs/common/resize-detector.js.map +1 -0
  50. package/dist/cjs/common/styles.js +135 -0
  51. package/dist/cjs/common/styles.js.map +1 -0
  52. package/dist/cjs/common/support.js +60 -0
  53. package/dist/cjs/common/support.js.map +1 -0
  54. package/dist/cjs/common/utils.js +193 -0
  55. package/dist/cjs/common/utils.js.map +1 -0
  56. package/dist/cjs/data-editor/copy-paste.js +269 -0
  57. package/dist/cjs/data-editor/copy-paste.js.map +1 -0
  58. package/dist/cjs/data-editor/data-editor-fns.js +190 -0
  59. package/dist/cjs/data-editor/data-editor-fns.js.map +1 -0
  60. package/dist/cjs/data-editor/data-editor-keybindings.js +126 -0
  61. package/dist/cjs/data-editor/data-editor-keybindings.js.map +1 -0
  62. package/dist/cjs/data-editor/data-editor.js +2892 -0
  63. package/dist/cjs/data-editor/data-editor.js.map +1 -0
  64. package/dist/cjs/data-editor/group-rename.css +2 -0
  65. package/dist/cjs/data-editor/group-rename.js +49 -0
  66. package/dist/cjs/data-editor/group-rename.js.map +1 -0
  67. package/dist/cjs/data-editor/row-grouping-api.js +34 -0
  68. package/dist/cjs/data-editor/row-grouping-api.js.map +1 -0
  69. package/dist/cjs/data-editor/row-grouping.js +189 -0
  70. package/dist/cjs/data-editor/row-grouping.js.map +1 -0
  71. package/dist/cjs/data-editor/use-autoscroll.js +36 -0
  72. package/dist/cjs/data-editor/use-autoscroll.js.map +1 -0
  73. package/dist/cjs/data-editor/use-cells-for-selection.js +53 -0
  74. package/dist/cjs/data-editor/use-cells-for-selection.js.map +1 -0
  75. package/dist/cjs/data-editor/use-column-sizer.js +189 -0
  76. package/dist/cjs/data-editor/use-column-sizer.js.map +1 -0
  77. package/dist/cjs/data-editor/use-initial-scroll-offset.js +81 -0
  78. package/dist/cjs/data-editor/use-initial-scroll-offset.js.map +1 -0
  79. package/dist/cjs/data-editor/use-rem-adjuster.js +29 -0
  80. package/dist/cjs/data-editor/use-rem-adjuster.js.map +1 -0
  81. package/dist/cjs/data-editor/visible-region.js +2 -0
  82. package/dist/cjs/data-editor/visible-region.js.map +1 -0
  83. package/dist/cjs/data-editor-all.js +19 -0
  84. package/dist/cjs/data-editor-all.js.map +1 -0
  85. package/dist/cjs/index.js +36 -0
  86. package/dist/cjs/index.js.map +1 -0
  87. package/dist/cjs/internal/click-outside-container/click-outside-container.js +34 -0
  88. package/dist/cjs/internal/click-outside-container/click-outside-container.js.map +1 -0
  89. package/dist/cjs/internal/data-editor-container/data-grid-container.js +36 -0
  90. package/dist/cjs/internal/data-editor-container/data-grid-container.js.map +1 -0
  91. package/dist/cjs/internal/data-grid/animation-manager.js +95 -0
  92. package/dist/cjs/internal/data-grid/animation-manager.js.map +1 -0
  93. package/dist/cjs/internal/data-grid/cell-set.js +56 -0
  94. package/dist/cjs/internal/data-grid/cell-set.js.map +1 -0
  95. package/dist/cjs/internal/data-grid/color-parser.js +122 -0
  96. package/dist/cjs/internal/data-grid/color-parser.js.map +1 -0
  97. package/dist/cjs/internal/data-grid/data-grid-sprites.js +64 -0
  98. package/dist/cjs/internal/data-grid/data-grid-sprites.js.map +1 -0
  99. package/dist/cjs/internal/data-grid/data-grid-types.js +299 -0
  100. package/dist/cjs/internal/data-grid/data-grid-types.js.map +1 -0
  101. package/dist/cjs/internal/data-grid/data-grid.js +1208 -0
  102. package/dist/cjs/internal/data-grid/data-grid.js.map +1 -0
  103. package/dist/cjs/internal/data-grid/event-args.js +30 -0
  104. package/dist/cjs/internal/data-grid/event-args.js.map +1 -0
  105. package/dist/cjs/internal/data-grid/image-window-loader-interface.js +2 -0
  106. package/dist/cjs/internal/data-grid/image-window-loader-interface.js.map +1 -0
  107. package/dist/cjs/internal/data-grid/render/data-grid-lib.js +652 -0
  108. package/dist/cjs/internal/data-grid/render/data-grid-lib.js.map +1 -0
  109. package/dist/cjs/internal/data-grid/render/data-grid-render.blit.js +218 -0
  110. package/dist/cjs/internal/data-grid/render/data-grid-render.blit.js.map +1 -0
  111. package/dist/cjs/internal/data-grid/render/data-grid-render.cells.js +369 -0
  112. package/dist/cjs/internal/data-grid/render/data-grid-render.cells.js.map +1 -0
  113. package/dist/cjs/internal/data-grid/render/data-grid-render.header.js +440 -0
  114. package/dist/cjs/internal/data-grid/render/data-grid-render.header.js.map +1 -0
  115. package/dist/cjs/internal/data-grid/render/data-grid-render.js +316 -0
  116. package/dist/cjs/internal/data-grid/render/data-grid-render.js.map +1 -0
  117. package/dist/cjs/internal/data-grid/render/data-grid-render.lines.js +256 -0
  118. package/dist/cjs/internal/data-grid/render/data-grid-render.lines.js.map +1 -0
  119. package/dist/cjs/internal/data-grid/render/data-grid-render.walk.js +157 -0
  120. package/dist/cjs/internal/data-grid/render/data-grid-render.walk.js.map +1 -0
  121. package/dist/cjs/internal/data-grid/render/data-grid.render.rings.js +203 -0
  122. package/dist/cjs/internal/data-grid/render/data-grid.render.rings.js.map +1 -0
  123. package/dist/cjs/internal/data-grid/render/draw-checkbox.js +65 -0
  124. package/dist/cjs/internal/data-grid/render/draw-checkbox.js.map +1 -0
  125. package/dist/cjs/internal/data-grid/render/draw-edit-hover-indicator.js +38 -0
  126. package/dist/cjs/internal/data-grid/render/draw-edit-hover-indicator.js.map +1 -0
  127. package/dist/cjs/internal/data-grid/render/draw-grid-arg.js +2 -0
  128. package/dist/cjs/internal/data-grid/render/draw-grid-arg.js.map +1 -0
  129. package/dist/cjs/internal/data-grid/sprites.js +288 -0
  130. package/dist/cjs/internal/data-grid/sprites.js.map +1 -0
  131. package/dist/cjs/internal/data-grid/use-animation-queue.js +33 -0
  132. package/dist/cjs/internal/data-grid/use-animation-queue.js.map +1 -0
  133. package/dist/cjs/internal/data-grid/use-selection-behavior.js +112 -0
  134. package/dist/cjs/internal/data-grid/use-selection-behavior.js.map +1 -0
  135. package/dist/cjs/internal/data-grid-dnd/data-grid-dnd.js +239 -0
  136. package/dist/cjs/internal/data-grid-dnd/data-grid-dnd.js.map +1 -0
  137. package/dist/cjs/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.js +77 -0
  138. package/dist/cjs/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.js.map +1 -0
  139. package/dist/cjs/internal/data-grid-overlay-editor/data-grid-overlay-editor.js +124 -0
  140. package/dist/cjs/internal/data-grid-overlay-editor/data-grid-overlay-editor.js.map +1 -0
  141. package/dist/cjs/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.js +34 -0
  142. package/dist/cjs/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.js.map +1 -0
  143. package/dist/cjs/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.js +10 -0
  144. package/dist/cjs/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.js.map +1 -0
  145. package/dist/cjs/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.js +50 -0
  146. package/dist/cjs/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.js.map +1 -0
  147. package/dist/cjs/internal/data-grid-overlay-editor/private/image-overlay-editor-style.js +56 -0
  148. package/dist/cjs/internal/data-grid-overlay-editor/private/image-overlay-editor-style.js.map +1 -0
  149. package/dist/cjs/internal/data-grid-overlay-editor/private/image-overlay-editor.js +21 -0
  150. package/dist/cjs/internal/data-grid-overlay-editor/private/image-overlay-editor.js.map +1 -0
  151. package/dist/cjs/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.js +76 -0
  152. package/dist/cjs/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.js.map +1 -0
  153. package/dist/cjs/internal/data-grid-overlay-editor/private/markdown-overlay-editor.js +32 -0
  154. package/dist/cjs/internal/data-grid-overlay-editor/private/markdown-overlay-editor.js.map +1 -0
  155. package/dist/cjs/internal/data-grid-overlay-editor/private/number-overlay-editor-style.js +15 -0
  156. package/dist/cjs/internal/data-grid-overlay-editor/private/number-overlay-editor-style.js.map +1 -0
  157. package/dist/cjs/internal/data-grid-overlay-editor/private/number-overlay-editor.js +30 -0
  158. package/dist/cjs/internal/data-grid-overlay-editor/private/number-overlay-editor.js.map +1 -0
  159. package/dist/cjs/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.js +53 -0
  160. package/dist/cjs/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.js.map +1 -0
  161. package/dist/cjs/internal/data-grid-overlay-editor/private/uri-overlay-editor.js +21 -0
  162. package/dist/cjs/internal/data-grid-overlay-editor/private/uri-overlay-editor.js.map +1 -0
  163. package/dist/cjs/internal/data-grid-overlay-editor/use-stay-on-screen.js +47 -0
  164. package/dist/cjs/internal/data-grid-overlay-editor/use-stay-on-screen.js.map +1 -0
  165. package/dist/cjs/internal/data-grid-search/data-grid-search-style.js +96 -0
  166. package/dist/cjs/internal/data-grid-search/data-grid-search-style.js.map +1 -0
  167. package/dist/cjs/internal/data-grid-search/data-grid-search.js +297 -0
  168. package/dist/cjs/internal/data-grid-search/data-grid-search.js.map +1 -0
  169. package/dist/cjs/internal/growing-entry/growing-entry-style.js +60 -0
  170. package/dist/cjs/internal/growing-entry/growing-entry-style.js.map +1 -0
  171. package/dist/cjs/internal/growing-entry/growing-entry.js +41 -0
  172. package/dist/cjs/internal/growing-entry/growing-entry.js.map +1 -0
  173. package/dist/cjs/internal/markdown-div/markdown-div.js +41 -0
  174. package/dist/cjs/internal/markdown-div/markdown-div.js.map +1 -0
  175. package/dist/cjs/internal/markdown-div/private/markdown-container.js +19 -0
  176. package/dist/cjs/internal/markdown-div/private/markdown-container.js.map +1 -0
  177. package/dist/cjs/internal/scrolling-data-grid/infinite-scroller.js +265 -0
  178. package/dist/cjs/internal/scrolling-data-grid/infinite-scroller.js.map +1 -0
  179. package/dist/cjs/internal/scrolling-data-grid/scrolling-data-grid.js +155 -0
  180. package/dist/cjs/internal/scrolling-data-grid/scrolling-data-grid.js.map +1 -0
  181. package/dist/cjs/internal/scrolling-data-grid/use-kinetic-scroll.js +65 -0
  182. package/dist/cjs/internal/scrolling-data-grid/use-kinetic-scroll.js.map +1 -0
  183. package/dist/dts/cells/boolean-cell.d.ts +4 -0
  184. package/dist/dts/cells/boolean-cell.d.ts.map +1 -0
  185. package/dist/dts/cells/bubble-cell.d.ts +4 -0
  186. package/dist/dts/cells/bubble-cell.d.ts.map +1 -0
  187. package/dist/dts/cells/cell-types.d.ts +89 -0
  188. package/dist/dts/cells/cell-types.d.ts.map +1 -0
  189. package/dist/dts/cells/drilldown-cell.d.ts +4 -0
  190. package/dist/dts/cells/drilldown-cell.d.ts.map +1 -0
  191. package/dist/dts/cells/image-cell.d.ts +5 -0
  192. package/dist/dts/cells/image-cell.d.ts.map +1 -0
  193. package/dist/dts/cells/index.d.ts +4 -0
  194. package/dist/dts/cells/index.d.ts.map +1 -0
  195. package/dist/dts/cells/loading-cell.d.ts +4 -0
  196. package/dist/dts/cells/loading-cell.d.ts.map +1 -0
  197. package/dist/dts/cells/markdown-cell.d.ts +4 -0
  198. package/dist/dts/cells/markdown-cell.d.ts.map +1 -0
  199. package/dist/dts/cells/marker-cell.d.ts +4 -0
  200. package/dist/dts/cells/marker-cell.d.ts.map +1 -0
  201. package/dist/dts/cells/new-row-cell.d.ts +4 -0
  202. package/dist/dts/cells/new-row-cell.d.ts.map +1 -0
  203. package/dist/dts/cells/number-cell.d.ts +4 -0
  204. package/dist/dts/cells/number-cell.d.ts.map +1 -0
  205. package/dist/dts/cells/protected-cell.d.ts +4 -0
  206. package/dist/dts/cells/protected-cell.d.ts.map +1 -0
  207. package/dist/dts/cells/row-id-cell.d.ts +4 -0
  208. package/dist/dts/cells/row-id-cell.d.ts.map +1 -0
  209. package/dist/dts/cells/text-cell.d.ts +4 -0
  210. package/dist/dts/cells/text-cell.d.ts.map +1 -0
  211. package/dist/dts/cells/uri-cell.d.ts +4 -0
  212. package/dist/dts/cells/uri-cell.d.ts.map +1 -0
  213. package/dist/dts/common/browser-detect.d.ts +11 -0
  214. package/dist/dts/common/browser-detect.d.ts.map +1 -0
  215. package/dist/dts/common/image-window-loader.d.ts +15 -0
  216. package/dist/dts/common/image-window-loader.d.ts.map +1 -0
  217. package/dist/dts/common/is-hotkey.d.ts +7 -0
  218. package/dist/dts/common/is-hotkey.d.ts.map +1 -0
  219. package/dist/dts/common/math.d.ts +20 -0
  220. package/dist/dts/common/math.d.ts.map +1 -0
  221. package/dist/dts/common/render-state-provider.d.ts +20 -0
  222. package/dist/dts/common/render-state-provider.d.ts.map +1 -0
  223. package/dist/dts/common/resize-detector.d.ts +11 -0
  224. package/dist/dts/common/resize-detector.d.ts.map +1 -0
  225. package/dist/dts/common/styles.d.ts +61 -0
  226. package/dist/dts/common/styles.d.ts.map +1 -0
  227. package/dist/dts/common/support.d.ts +13 -0
  228. package/dist/dts/common/support.d.ts.map +1 -0
  229. package/dist/dts/common/utils.d.ts +38 -0
  230. package/dist/dts/common/utils.d.ts.map +1 -0
  231. package/dist/dts/data-editor/copy-paste.d.ts +22 -0
  232. package/dist/dts/data-editor/copy-paste.d.ts.map +1 -0
  233. package/dist/dts/data-editor/data-editor-fns.d.ts +16 -0
  234. package/dist/dts/data-editor/data-editor-fns.d.ts.map +1 -0
  235. package/dist/dts/data-editor/data-editor-keybindings.d.ts +62 -0
  236. package/dist/dts/data-editor/data-editor-keybindings.d.ts.map +1 -0
  237. package/dist/dts/data-editor/data-editor.d.ts +532 -0
  238. package/dist/dts/data-editor/data-editor.d.ts.map +1 -0
  239. package/dist/dts/data-editor/group-rename.d.ts +12 -0
  240. package/dist/dts/data-editor/group-rename.d.ts.map +1 -0
  241. package/dist/dts/data-editor/row-grouping-api.d.ts +21 -0
  242. package/dist/dts/data-editor/row-grouping-api.d.ts.map +1 -0
  243. package/dist/dts/data-editor/row-grouping.d.ts +82 -0
  244. package/dist/dts/data-editor/row-grouping.d.ts.map +1 -0
  245. package/dist/dts/data-editor/use-autoscroll.d.ts +4 -0
  246. package/dist/dts/data-editor/use-autoscroll.d.ts.map +1 -0
  247. package/dist/dts/data-editor/use-cells-for-selection.d.ts +7 -0
  248. package/dist/dts/data-editor/use-cells-for-selection.d.ts.map +1 -0
  249. package/dist/dts/data-editor/use-column-sizer.d.ts +11 -0
  250. package/dist/dts/data-editor/use-column-sizer.d.ts.map +1 -0
  251. package/dist/dts/data-editor/use-initial-scroll-offset.d.ts +9 -0
  252. package/dist/dts/data-editor/use-initial-scroll-offset.d.ts.map +1 -0
  253. package/dist/dts/data-editor/use-rem-adjuster.d.ts +22 -0
  254. package/dist/dts/data-editor/use-rem-adjuster.d.ts.map +1 -0
  255. package/dist/dts/data-editor/visible-region.d.ts +19 -0
  256. package/dist/dts/data-editor/visible-region.d.ts.map +1 -0
  257. package/dist/dts/data-editor-all.d.ts +8 -0
  258. package/dist/dts/data-editor-all.d.ts.map +1 -0
  259. package/dist/dts/index.d.ts +52 -0
  260. package/dist/dts/index.d.ts.map +1 -0
  261. package/dist/dts/internal/click-outside-container/click-outside-container.d.ts +15 -0
  262. package/dist/dts/internal/click-outside-container/click-outside-container.d.ts.map +1 -0
  263. package/dist/dts/internal/data-editor-container/data-grid-container.d.ts +10 -0
  264. package/dist/dts/internal/data-editor-container/data-grid-container.d.ts.map +1 -0
  265. package/dist/dts/internal/data-grid/animation-manager.d.ts +26 -0
  266. package/dist/dts/internal/data-grid/animation-manager.d.ts.map +1 -0
  267. package/dist/dts/internal/data-grid/cell-set.d.ts +17 -0
  268. package/dist/dts/internal/data-grid/cell-set.d.ts.map +1 -0
  269. package/dist/dts/internal/data-grid/color-parser.d.ts +16 -0
  270. package/dist/dts/internal/data-grid/color-parser.d.ts.map +1 -0
  271. package/dist/dts/internal/data-grid/data-grid-sprites.d.ts +35 -0
  272. package/dist/dts/internal/data-grid/data-grid-sprites.d.ts.map +1 -0
  273. package/dist/dts/internal/data-grid/data-grid-types.d.ts +443 -0
  274. package/dist/dts/internal/data-grid/data-grid-types.d.ts.map +1 -0
  275. package/dist/dts/internal/data-grid/data-grid.d.ts +244 -0
  276. package/dist/dts/internal/data-grid/data-grid.d.ts.map +1 -0
  277. package/dist/dts/internal/data-grid/event-args.d.ts +117 -0
  278. package/dist/dts/internal/data-grid/event-args.d.ts.map +1 -0
  279. package/dist/dts/internal/data-grid/image-window-loader-interface.d.ts +9 -0
  280. package/dist/dts/internal/data-grid/image-window-loader-interface.d.ts.map +1 -0
  281. package/dist/dts/internal/data-grid/render/data-grid-lib.d.ts +60 -0
  282. package/dist/dts/internal/data-grid/render/data-grid-lib.d.ts.map +1 -0
  283. package/dist/dts/internal/data-grid/render/data-grid-render.blit.d.ts +20 -0
  284. package/dist/dts/internal/data-grid/render/data-grid-render.blit.d.ts.map +1 -0
  285. package/dist/dts/internal/data-grid/render/data-grid-render.cells.d.ts +32 -0
  286. package/dist/dts/internal/data-grid/render/data-grid-render.cells.d.ts.map +1 -0
  287. package/dist/dts/internal/data-grid/render/data-grid-render.d.ts +3 -0
  288. package/dist/dts/internal/data-grid/render/data-grid-render.d.ts.map +1 -0
  289. package/dist/dts/internal/data-grid/render/data-grid-render.header.d.ts +22 -0
  290. package/dist/dts/internal/data-grid/render/data-grid-render.header.d.ts.map +1 -0
  291. package/dist/dts/internal/data-grid/render/data-grid-render.lines.d.ts +10 -0
  292. package/dist/dts/internal/data-grid/render/data-grid-render.lines.d.ts.map +1 -0
  293. package/dist/dts/internal/data-grid/render/data-grid-render.walk.d.ts +14 -0
  294. package/dist/dts/internal/data-grid/render/data-grid-render.walk.d.ts.map +1 -0
  295. package/dist/dts/internal/data-grid/render/data-grid.render.rings.d.ts +8 -0
  296. package/dist/dts/internal/data-grid/render/data-grid.render.rings.d.ts.map +1 -0
  297. package/dist/dts/internal/data-grid/render/draw-checkbox.d.ts +4 -0
  298. package/dist/dts/internal/data-grid/render/draw-checkbox.d.ts.map +1 -0
  299. package/dist/dts/internal/data-grid/render/draw-edit-hover-indicator.d.ts +4 -0
  300. package/dist/dts/internal/data-grid/render/draw-edit-hover-indicator.d.ts.map +1 -0
  301. package/dist/dts/internal/data-grid/render/draw-grid-arg.d.ts +73 -0
  302. package/dist/dts/internal/data-grid/render/draw-grid-arg.d.ts.map +1 -0
  303. package/dist/dts/internal/data-grid/sprites.d.ts +34 -0
  304. package/dist/dts/internal/data-grid/sprites.d.ts.map +1 -0
  305. package/dist/dts/internal/data-grid/use-animation-queue.d.ts +5 -0
  306. package/dist/dts/internal/data-grid/use-animation-queue.d.ts.map +1 -0
  307. package/dist/dts/internal/data-grid/use-selection-behavior.d.ts +13 -0
  308. package/dist/dts/internal/data-grid/use-selection-behavior.d.ts.map +1 -0
  309. package/dist/dts/internal/data-grid-dnd/data-grid-dnd.d.ts +63 -0
  310. package/dist/dts/internal/data-grid-dnd/data-grid-dnd.d.ts.map +1 -0
  311. package/dist/dts/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.d.ts +9 -0
  312. package/dist/dts/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.d.ts.map +1 -0
  313. package/dist/dts/internal/data-grid-overlay-editor/data-grid-overlay-editor.d.ts +32 -0
  314. package/dist/dts/internal/data-grid-overlay-editor/data-grid-overlay-editor.d.ts.map +1 -0
  315. package/dist/dts/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.d.ts +2 -0
  316. package/dist/dts/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.d.ts.map +1 -0
  317. package/dist/dts/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.d.ts +7 -0
  318. package/dist/dts/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.d.ts.map +1 -0
  319. package/dist/dts/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.d.ts +8 -0
  320. package/dist/dts/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.d.ts.map +1 -0
  321. package/dist/dts/internal/data-grid-overlay-editor/private/image-overlay-editor-style.d.ts +2 -0
  322. package/dist/dts/internal/data-grid-overlay-editor/private/image-overlay-editor-style.d.ts.map +1 -0
  323. package/dist/dts/internal/data-grid-overlay-editor/private/image-overlay-editor.d.ts +13 -0
  324. package/dist/dts/internal/data-grid-overlay-editor/private/image-overlay-editor.d.ts.map +1 -0
  325. package/dist/dts/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.d.ts +6 -0
  326. package/dist/dts/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.d.ts.map +1 -0
  327. package/dist/dts/internal/data-grid-overlay-editor/private/markdown-overlay-editor.d.ts +14 -0
  328. package/dist/dts/internal/data-grid-overlay-editor/private/markdown-overlay-editor.d.ts.map +1 -0
  329. package/dist/dts/internal/data-grid-overlay-editor/private/number-overlay-editor-style.d.ts +2 -0
  330. package/dist/dts/internal/data-grid-overlay-editor/private/number-overlay-editor-style.d.ts.map +1 -0
  331. package/dist/dts/internal/data-grid-overlay-editor/private/number-overlay-editor.d.ts +17 -0
  332. package/dist/dts/internal/data-grid-overlay-editor/private/number-overlay-editor.d.ts.map +1 -0
  333. package/dist/dts/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.d.ts +2 -0
  334. package/dist/dts/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.d.ts.map +1 -0
  335. package/dist/dts/internal/data-grid-overlay-editor/private/uri-overlay-editor.d.ts +13 -0
  336. package/dist/dts/internal/data-grid-overlay-editor/private/uri-overlay-editor.d.ts.map +1 -0
  337. package/dist/dts/internal/data-grid-overlay-editor/use-stay-on-screen.d.ts +8 -0
  338. package/dist/dts/internal/data-grid-overlay-editor/use-stay-on-screen.d.ts.map +1 -0
  339. package/dist/dts/internal/data-grid-search/data-grid-search-style.d.ts +2 -0
  340. package/dist/dts/internal/data-grid-search/data-grid-search-style.d.ts.map +1 -0
  341. package/dist/dts/internal/data-grid-search/data-grid-search.d.ts +41 -0
  342. package/dist/dts/internal/data-grid-search/data-grid-search.d.ts.map +1 -0
  343. package/dist/dts/internal/growing-entry/growing-entry-style.d.ts +4 -0
  344. package/dist/dts/internal/growing-entry/growing-entry-style.d.ts.map +1 -0
  345. package/dist/dts/internal/growing-entry/growing-entry.d.ts +12 -0
  346. package/dist/dts/internal/growing-entry/growing-entry.d.ts.map +1 -0
  347. package/dist/dts/internal/markdown-div/markdown-div.d.ts +14 -0
  348. package/dist/dts/internal/markdown-div/markdown-div.d.ts.map +1 -0
  349. package/dist/dts/internal/markdown-div/private/markdown-container.d.ts +2 -0
  350. package/dist/dts/internal/markdown-div/private/markdown-container.d.ts.map +1 -0
  351. package/dist/dts/internal/scrolling-data-grid/infinite-scroller.d.ts +38 -0
  352. package/dist/dts/internal/scrolling-data-grid/infinite-scroller.d.ts.map +1 -0
  353. package/dist/dts/internal/scrolling-data-grid/scrolling-data-grid.d.ts +57 -0
  354. package/dist/dts/internal/scrolling-data-grid/scrolling-data-grid.d.ts.map +1 -0
  355. package/dist/dts/internal/scrolling-data-grid/use-kinetic-scroll.d.ts +3 -0
  356. package/dist/dts/internal/scrolling-data-grid/use-kinetic-scroll.d.ts.map +1 -0
  357. package/dist/esm/cells/boolean-cell.js +87 -0
  358. package/dist/esm/cells/boolean-cell.js.map +1 -0
  359. package/dist/esm/cells/bubble-cell.js +53 -0
  360. package/dist/esm/cells/bubble-cell.js.map +1 -0
  361. package/dist/esm/cells/cell-types.js +2 -0
  362. package/dist/esm/cells/cell-types.js.map +1 -0
  363. package/dist/esm/cells/drilldown-cell.js +171 -0
  364. package/dist/esm/cells/drilldown-cell.js.map +1 -0
  365. package/dist/esm/cells/image-cell.js +94 -0
  366. package/dist/esm/cells/image-cell.js.map +1 -0
  367. package/dist/esm/cells/index.js +30 -0
  368. package/dist/esm/cells/index.js.map +1 -0
  369. package/dist/esm/cells/loading-cell.js +41 -0
  370. package/dist/esm/cells/loading-cell.js.map +1 -0
  371. package/dist/esm/cells/markdown-cell.js +30 -0
  372. package/dist/esm/cells/markdown-cell.js.map +1 -0
  373. package/dist/esm/cells/marker-cell.js +80 -0
  374. package/dist/esm/cells/marker-cell.js.map +1 -0
  375. package/dist/esm/cells/new-row-cell.js +53 -0
  376. package/dist/esm/cells/new-row-cell.js.map +1 -0
  377. package/dist/esm/cells/number-cell.js +44 -0
  378. package/dist/esm/cells/number-cell.js.map +1 -0
  379. package/dist/esm/cells/protected-cell.js +35 -0
  380. package/dist/esm/cells/protected-cell.js.map +1 -0
  381. package/dist/esm/cells/row-id-cell.js +23 -0
  382. package/dist/esm/cells/row-id-cell.js.map +1 -0
  383. package/dist/esm/cells/text-cell.js +48 -0
  384. package/dist/esm/cells/text-cell.js.map +1 -0
  385. package/dist/esm/cells/uri-cell.js +104 -0
  386. package/dist/esm/cells/uri-cell.js.map +1 -0
  387. package/dist/esm/common/browser-detect.js +20 -0
  388. package/dist/esm/common/browser-detect.js.map +1 -0
  389. package/dist/esm/common/image-window-loader.js +98 -0
  390. package/dist/esm/common/image-window-loader.js.map +1 -0
  391. package/dist/esm/common/is-hotkey.js +76 -0
  392. package/dist/esm/common/is-hotkey.js.map +1 -0
  393. package/dist/esm/common/math.js +297 -0
  394. package/dist/esm/common/math.js.map +1 -0
  395. package/dist/esm/common/render-state-provider.js +70 -0
  396. package/dist/esm/common/render-state-provider.js.map +1 -0
  397. package/dist/esm/common/resize-detector.js +27 -0
  398. package/dist/esm/common/resize-detector.js.map +1 -0
  399. package/dist/esm/common/styles.js +135 -0
  400. package/dist/esm/common/styles.js.map +1 -0
  401. package/dist/esm/common/support.js +60 -0
  402. package/dist/esm/common/support.js.map +1 -0
  403. package/dist/esm/common/utils.js +193 -0
  404. package/dist/esm/common/utils.js.map +1 -0
  405. package/dist/esm/data-editor/copy-paste.js +269 -0
  406. package/dist/esm/data-editor/copy-paste.js.map +1 -0
  407. package/dist/esm/data-editor/data-editor-fns.js +197 -0
  408. package/dist/esm/data-editor/data-editor-fns.js.map +1 -0
  409. package/dist/esm/data-editor/data-editor-keybindings.js +126 -0
  410. package/dist/esm/data-editor/data-editor-keybindings.js.map +1 -0
  411. package/dist/esm/data-editor/data-editor.js +2892 -0
  412. package/dist/esm/data-editor/data-editor.js.map +1 -0
  413. package/dist/esm/data-editor/group-rename.css +2 -0
  414. package/dist/esm/data-editor/group-rename.js +49 -0
  415. package/dist/esm/data-editor/group-rename.js.map +1 -0
  416. package/dist/esm/data-editor/row-grouping-api.js +34 -0
  417. package/dist/esm/data-editor/row-grouping-api.js.map +1 -0
  418. package/dist/esm/data-editor/row-grouping.js +189 -0
  419. package/dist/esm/data-editor/row-grouping.js.map +1 -0
  420. package/dist/esm/data-editor/use-autoscroll.js +36 -0
  421. package/dist/esm/data-editor/use-autoscroll.js.map +1 -0
  422. package/dist/esm/data-editor/use-cells-for-selection.js +53 -0
  423. package/dist/esm/data-editor/use-cells-for-selection.js.map +1 -0
  424. package/dist/esm/data-editor/use-column-sizer.js +189 -0
  425. package/dist/esm/data-editor/use-column-sizer.js.map +1 -0
  426. package/dist/esm/data-editor/use-initial-scroll-offset.js +81 -0
  427. package/dist/esm/data-editor/use-initial-scroll-offset.js.map +1 -0
  428. package/dist/esm/data-editor/use-rem-adjuster.js +29 -0
  429. package/dist/esm/data-editor/use-rem-adjuster.js.map +1 -0
  430. package/dist/esm/data-editor/visible-region.js +2 -0
  431. package/dist/esm/data-editor/visible-region.js.map +1 -0
  432. package/dist/esm/data-editor-all.js +19 -0
  433. package/dist/esm/data-editor-all.js.map +1 -0
  434. package/dist/esm/index.js +36 -0
  435. package/dist/esm/index.js.map +1 -0
  436. package/dist/esm/internal/click-outside-container/click-outside-container.js +34 -0
  437. package/dist/esm/internal/click-outside-container/click-outside-container.js.map +1 -0
  438. package/dist/esm/internal/data-editor-container/data-grid-container.js +36 -0
  439. package/dist/esm/internal/data-editor-container/data-grid-container.js.map +1 -0
  440. package/dist/esm/internal/data-grid/animation-manager.js +95 -0
  441. package/dist/esm/internal/data-grid/animation-manager.js.map +1 -0
  442. package/dist/esm/internal/data-grid/cell-set.js +56 -0
  443. package/dist/esm/internal/data-grid/cell-set.js.map +1 -0
  444. package/dist/esm/internal/data-grid/color-parser.js +122 -0
  445. package/dist/esm/internal/data-grid/color-parser.js.map +1 -0
  446. package/dist/esm/internal/data-grid/data-grid-sprites.js +65 -0
  447. package/dist/esm/internal/data-grid/data-grid-sprites.js.map +1 -0
  448. package/dist/esm/internal/data-grid/data-grid-types.js +299 -0
  449. package/dist/esm/internal/data-grid/data-grid-types.js.map +1 -0
  450. package/dist/esm/internal/data-grid/data-grid.js +1209 -0
  451. package/dist/esm/internal/data-grid/data-grid.js.map +1 -0
  452. package/dist/esm/internal/data-grid/event-args.js +30 -0
  453. package/dist/esm/internal/data-grid/event-args.js.map +1 -0
  454. package/dist/esm/internal/data-grid/image-window-loader-interface.js +2 -0
  455. package/dist/esm/internal/data-grid/image-window-loader-interface.js.map +1 -0
  456. package/dist/esm/internal/data-grid/render/data-grid-lib.js +653 -0
  457. package/dist/esm/internal/data-grid/render/data-grid-lib.js.map +1 -0
  458. package/dist/esm/internal/data-grid/render/data-grid-render.blit.js +219 -0
  459. package/dist/esm/internal/data-grid/render/data-grid-render.blit.js.map +1 -0
  460. package/dist/esm/internal/data-grid/render/data-grid-render.cells.js +370 -0
  461. package/dist/esm/internal/data-grid/render/data-grid-render.cells.js.map +1 -0
  462. package/dist/esm/internal/data-grid/render/data-grid-render.header.js +440 -0
  463. package/dist/esm/internal/data-grid/render/data-grid-render.header.js.map +1 -0
  464. package/dist/esm/internal/data-grid/render/data-grid-render.js +320 -0
  465. package/dist/esm/internal/data-grid/render/data-grid-render.js.map +1 -0
  466. package/dist/esm/internal/data-grid/render/data-grid-render.lines.js +261 -0
  467. package/dist/esm/internal/data-grid/render/data-grid-render.lines.js.map +1 -0
  468. package/dist/esm/internal/data-grid/render/data-grid-render.walk.js +158 -0
  469. package/dist/esm/internal/data-grid/render/data-grid-render.walk.js.map +1 -0
  470. package/dist/esm/internal/data-grid/render/data-grid.render.rings.js +205 -0
  471. package/dist/esm/internal/data-grid/render/data-grid.render.rings.js.map +1 -0
  472. package/dist/esm/internal/data-grid/render/draw-checkbox.js +65 -0
  473. package/dist/esm/internal/data-grid/render/draw-checkbox.js.map +1 -0
  474. package/dist/esm/internal/data-grid/render/draw-edit-hover-indicator.js +38 -0
  475. package/dist/esm/internal/data-grid/render/draw-edit-hover-indicator.js.map +1 -0
  476. package/dist/esm/internal/data-grid/render/draw-grid-arg.js +2 -0
  477. package/dist/esm/internal/data-grid/render/draw-grid-arg.js.map +1 -0
  478. package/dist/esm/internal/data-grid/sprites.js +288 -0
  479. package/dist/esm/internal/data-grid/sprites.js.map +1 -0
  480. package/dist/esm/internal/data-grid/use-animation-queue.js +34 -0
  481. package/dist/esm/internal/data-grid/use-animation-queue.js.map +1 -0
  482. package/dist/esm/internal/data-grid/use-selection-behavior.js +112 -0
  483. package/dist/esm/internal/data-grid/use-selection-behavior.js.map +1 -0
  484. package/dist/esm/internal/data-grid-dnd/data-grid-dnd.js +239 -0
  485. package/dist/esm/internal/data-grid-dnd/data-grid-dnd.js.map +1 -0
  486. package/dist/esm/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.js +77 -0
  487. package/dist/esm/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.js.map +1 -0
  488. package/dist/esm/internal/data-grid-overlay-editor/data-grid-overlay-editor.js +124 -0
  489. package/dist/esm/internal/data-grid-overlay-editor/data-grid-overlay-editor.js.map +1 -0
  490. package/dist/esm/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.js +34 -0
  491. package/dist/esm/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.js.map +1 -0
  492. package/dist/esm/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.js +10 -0
  493. package/dist/esm/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.js.map +1 -0
  494. package/dist/esm/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.js +50 -0
  495. package/dist/esm/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.js.map +1 -0
  496. package/dist/esm/internal/data-grid-overlay-editor/private/image-overlay-editor-style.js +56 -0
  497. package/dist/esm/internal/data-grid-overlay-editor/private/image-overlay-editor-style.js.map +1 -0
  498. package/dist/esm/internal/data-grid-overlay-editor/private/image-overlay-editor.js +21 -0
  499. package/dist/esm/internal/data-grid-overlay-editor/private/image-overlay-editor.js.map +1 -0
  500. package/dist/esm/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.js +76 -0
  501. package/dist/esm/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.js.map +1 -0
  502. package/dist/esm/internal/data-grid-overlay-editor/private/markdown-overlay-editor.js +32 -0
  503. package/dist/esm/internal/data-grid-overlay-editor/private/markdown-overlay-editor.js.map +1 -0
  504. package/dist/esm/internal/data-grid-overlay-editor/private/number-overlay-editor-style.js +15 -0
  505. package/dist/esm/internal/data-grid-overlay-editor/private/number-overlay-editor-style.js.map +1 -0
  506. package/dist/esm/internal/data-grid-overlay-editor/private/number-overlay-editor.js +30 -0
  507. package/dist/esm/internal/data-grid-overlay-editor/private/number-overlay-editor.js.map +1 -0
  508. package/dist/esm/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.js +53 -0
  509. package/dist/esm/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.js.map +1 -0
  510. package/dist/esm/internal/data-grid-overlay-editor/private/uri-overlay-editor.js +21 -0
  511. package/dist/esm/internal/data-grid-overlay-editor/private/uri-overlay-editor.js.map +1 -0
  512. package/dist/esm/internal/data-grid-overlay-editor/use-stay-on-screen.js +47 -0
  513. package/dist/esm/internal/data-grid-overlay-editor/use-stay-on-screen.js.map +1 -0
  514. package/dist/esm/internal/data-grid-search/data-grid-search-style.js +96 -0
  515. package/dist/esm/internal/data-grid-search/data-grid-search-style.js.map +1 -0
  516. package/dist/esm/internal/data-grid-search/data-grid-search.js +297 -0
  517. package/dist/esm/internal/data-grid-search/data-grid-search.js.map +1 -0
  518. package/dist/esm/internal/growing-entry/growing-entry-style.js +60 -0
  519. package/dist/esm/internal/growing-entry/growing-entry-style.js.map +1 -0
  520. package/dist/esm/internal/growing-entry/growing-entry.js +41 -0
  521. package/dist/esm/internal/growing-entry/growing-entry.js.map +1 -0
  522. package/dist/esm/internal/markdown-div/markdown-div.js +41 -0
  523. package/dist/esm/internal/markdown-div/markdown-div.js.map +1 -0
  524. package/dist/esm/internal/markdown-div/private/markdown-container.js +19 -0
  525. package/dist/esm/internal/markdown-div/private/markdown-container.js.map +1 -0
  526. package/dist/esm/internal/scrolling-data-grid/infinite-scroller.js +265 -0
  527. package/dist/esm/internal/scrolling-data-grid/infinite-scroller.js.map +1 -0
  528. package/dist/esm/internal/scrolling-data-grid/scrolling-data-grid.js +155 -0
  529. package/dist/esm/internal/scrolling-data-grid/scrolling-data-grid.js.map +1 -0
  530. package/dist/esm/internal/scrolling-data-grid/use-kinetic-scroll.js +65 -0
  531. package/dist/esm/internal/scrolling-data-grid/use-kinetic-scroll.js.map +1 -0
  532. package/dist/index.css +2 -0
  533. package/package.json +81 -0
  534. package/src/cells/boolean-cell.tsx +135 -0
  535. package/src/cells/bubble-cell.tsx +68 -0
  536. package/src/cells/cell-types.ts +124 -0
  537. package/src/cells/drilldown-cell.tsx +244 -0
  538. package/src/cells/image-cell.tsx +117 -0
  539. package/src/cells/index.ts +31 -0
  540. package/src/cells/loading-cell.tsx +56 -0
  541. package/src/cells/markdown-cell.tsx +44 -0
  542. package/src/cells/marker-cell.tsx +110 -0
  543. package/src/cells/new-row-cell.tsx +60 -0
  544. package/src/cells/number-cell.tsx +64 -0
  545. package/src/cells/protected-cell.tsx +42 -0
  546. package/src/cells/row-id-cell.tsx +35 -0
  547. package/src/cells/text-cell.tsx +63 -0
  548. package/src/cells/uri-cell.tsx +155 -0
  549. package/src/common/browser-detect.ts +25 -0
  550. package/src/common/image-window-loader.ts +114 -0
  551. package/src/common/is-hotkey.ts +86 -0
  552. package/src/common/math.ts +357 -0
  553. package/src/common/render-state-provider.ts +87 -0
  554. package/src/common/resize-detector.ts +43 -0
  555. package/src/common/styles.ts +214 -0
  556. package/src/common/support.ts +67 -0
  557. package/src/common/utils.tsx +284 -0
  558. package/src/data-editor/copy-paste.ts +320 -0
  559. package/src/data-editor/data-editor-fns.ts +227 -0
  560. package/src/data-editor/data-editor-keybindings.ts +198 -0
  561. package/src/data-editor/data-editor.tsx +4382 -0
  562. package/src/data-editor/group-rename.tsx +67 -0
  563. package/src/data-editor/row-grouping-api.ts +72 -0
  564. package/src/data-editor/row-grouping.ts +326 -0
  565. package/src/data-editor/stories/data-editor-repros.stories.tsx +107 -0
  566. package/src/data-editor/stories/data-editor.stories.tsx +796 -0
  567. package/src/data-editor/stories/utils.tsx +827 -0
  568. package/src/data-editor/use-autoscroll.ts +41 -0
  569. package/src/data-editor/use-cells-for-selection.ts +72 -0
  570. package/src/data-editor/use-column-sizer.ts +253 -0
  571. package/src/data-editor/use-initial-scroll-offset.ts +102 -0
  572. package/src/data-editor/use-rem-adjuster.ts +59 -0
  573. package/src/data-editor/visible-region.ts +20 -0
  574. package/src/data-editor-all.tsx +36 -0
  575. package/src/docs/00-faq.stories.tsx +63 -0
  576. package/src/docs/01-getting-started.stories.tsx +299 -0
  577. package/src/docs/02-editing-data.stories.tsx +365 -0
  578. package/src/docs/03-grid-column.stories.tsx +146 -0
  579. package/src/docs/04-streaming-data.stories.tsx +434 -0
  580. package/src/docs/05-copy-paste.stories.tsx.tsx +279 -0
  581. package/src/docs/06-search.stories.tsx +219 -0
  582. package/src/docs/07-column-grouping.stories.tsx +212 -0
  583. package/src/docs/08-theming.stories.tsx +409 -0
  584. package/src/docs/09-menus.stories.tsx +344 -0
  585. package/src/docs/doc-wrapper.tsx +512 -0
  586. package/src/docs/examples/add-column.stories.tsx +62 -0
  587. package/src/docs/examples/add-data-to-middle.stories.tsx +93 -0
  588. package/src/docs/examples/add-data-to-top.stories.tsx +70 -0
  589. package/src/docs/examples/add-data.stories.tsx +74 -0
  590. package/src/docs/examples/all-cell-kinds.stories.tsx +61 -0
  591. package/src/docs/examples/append-row-handle.stories.tsx +79 -0
  592. package/src/docs/examples/automatic-row-markers.stories.tsx +57 -0
  593. package/src/docs/examples/built-in-search.stories.tsx +83 -0
  594. package/src/docs/examples/cell-activated-event.stories.tsx +92 -0
  595. package/src/docs/examples/column-group-collapse.stories.tsx +105 -0
  596. package/src/docs/examples/column-groups.stories.tsx +51 -0
  597. package/src/docs/examples/content-alignment.stories.tsx +64 -0
  598. package/src/docs/examples/controlled-search.stories.tsx +93 -0
  599. package/src/docs/examples/controlled-selection.stories.tsx +98 -0
  600. package/src/docs/examples/copy-support.stories.tsx +63 -0
  601. package/src/docs/examples/custom-editors.stories.tsx +90 -0
  602. package/src/docs/examples/custom-event-target.stories.tsx +157 -0
  603. package/src/docs/examples/custom-group-header.stories.tsx +423 -0
  604. package/src/docs/examples/custom-header-icons.stories.tsx +76 -0
  605. package/src/docs/examples/custom-header.stories.tsx +67 -0
  606. package/src/docs/examples/custom-renderers.stories.tsx +72 -0
  607. package/src/docs/examples/drag-source.stories.tsx +63 -0
  608. package/src/docs/examples/drop-events.stories.tsx +155 -0
  609. package/src/docs/examples/fill-handle.stories.tsx +118 -0
  610. package/src/docs/examples/freeze-columns.stories.tsx +59 -0
  611. package/src/docs/examples/freeze-rows.stories.tsx +71 -0
  612. package/src/docs/examples/get-mouse-args.stories.tsx +61 -0
  613. package/src/docs/examples/header-menus.stories.tsx +162 -0
  614. package/src/docs/examples/highlight-cells.stories.tsx +84 -0
  615. package/src/docs/examples/imperative-scroll.stories.tsx +96 -0
  616. package/src/docs/examples/input-blending.stories.tsx +116 -0
  617. package/src/docs/examples/keybindings.stories.tsx +90 -0
  618. package/src/docs/examples/layout-integration.stories.tsx +48 -0
  619. package/src/docs/examples/multi-level-column-groups.stories.tsx +119 -0
  620. package/src/docs/examples/multi-select-columns.stories.tsx +47 -0
  621. package/src/docs/examples/new-column-button.stories.tsx +56 -0
  622. package/src/docs/examples/obscured-grid.stories.tsx +70 -0
  623. package/src/docs/examples/observe-visible-region.stories.tsx +56 -0
  624. package/src/docs/examples/one-hundred-thousand-columns.stories.tsx +31 -0
  625. package/src/docs/examples/one-million-rows.stories.tsx +37 -0
  626. package/src/docs/examples/overscroll.stories.tsx +74 -0
  627. package/src/docs/examples/padding.stories.tsx +74 -0
  628. package/src/docs/examples/paste-support.stories.tsx +73 -0
  629. package/src/docs/examples/prevent-diagonal-scroll.stories.tsx +47 -0
  630. package/src/docs/examples/rapid-updates.stories.tsx +108 -0
  631. package/src/docs/examples/rearrange-columns.stories.tsx +76 -0
  632. package/src/docs/examples/reorder-rows.stories.tsx +83 -0
  633. package/src/docs/examples/resizable-columns.stories.tsx +67 -0
  634. package/src/docs/examples/right-element.stories.tsx +87 -0
  635. package/src/docs/examples/right-to-left.stories.tsx +73 -0
  636. package/src/docs/examples/row-and-header-sizes.stories.tsx +75 -0
  637. package/src/docs/examples/row-grouping.stories.tsx +142 -0
  638. package/src/docs/examples/row-hover.stories.tsx +67 -0
  639. package/src/docs/examples/row-markers.stories.tsx +74 -0
  640. package/src/docs/examples/row-selections.stories.tsx +84 -0
  641. package/src/docs/examples/scaled-view.stories.tsx +36 -0
  642. package/src/docs/examples/scroll-offset.stories.tsx +49 -0
  643. package/src/docs/examples/scroll-shadows.stories.tsx +83 -0
  644. package/src/docs/examples/search-as-filter.stories.tsx +76 -0
  645. package/src/docs/examples/selection-serialization.stories.tsx +208 -0
  646. package/src/docs/examples/server-side-data.stories.tsx +219 -0
  647. package/src/docs/examples/shadow-dom.stories.tsx +107 -0
  648. package/src/docs/examples/silly-numbers.stories.tsx +40 -0
  649. package/src/docs/examples/small-editable-grid.stories.tsx +39 -0
  650. package/src/docs/examples/smooth-scrolling-grid.stories.tsx +56 -0
  651. package/src/docs/examples/span-cell.stories.tsx +99 -0
  652. package/src/docs/examples/stretch-column-size.stories.tsx +55 -0
  653. package/src/docs/examples/ten-million-cells.stories.tsx +34 -0
  654. package/src/docs/examples/theme-per-column.stories.tsx +88 -0
  655. package/src/docs/examples/theme-per-row.stories.tsx +73 -0
  656. package/src/docs/examples/theme-support.stories.tsx +150 -0
  657. package/src/docs/examples/tooltips.stories.tsx +119 -0
  658. package/src/docs/examples/trailing-row-options.stories.tsx +106 -0
  659. package/src/docs/examples/uneven-rows.stories.tsx +44 -0
  660. package/src/docs/examples/validate-data.stories.tsx +63 -0
  661. package/src/docs/examples/wrapping-text.stories.tsx +97 -0
  662. package/src/docs/template.tsx +69 -0
  663. package/src/index.ts +87 -0
  664. package/src/internal/click-outside-container/click-outside-container.tsx +50 -0
  665. package/src/internal/data-editor-container/data-grid-container.tsx +48 -0
  666. package/src/internal/data-grid/animation-manager.ts +119 -0
  667. package/src/internal/data-grid/cell-set.ts +65 -0
  668. package/src/internal/data-grid/color-parser.ts +141 -0
  669. package/src/internal/data-grid/data-grid-sprites.ts +107 -0
  670. package/src/internal/data-grid/data-grid-types.ts +742 -0
  671. package/src/internal/data-grid/data-grid.stories.tsx +410 -0
  672. package/src/internal/data-grid/data-grid.tsx +1978 -0
  673. package/src/internal/data-grid/event-args.ts +157 -0
  674. package/src/internal/data-grid/image-window-loader-interface.ts +9 -0
  675. package/src/internal/data-grid/render/data-grid-lib.ts +906 -0
  676. package/src/internal/data-grid/render/data-grid-render.blit.ts +291 -0
  677. package/src/internal/data-grid/render/data-grid-render.cells.ts +564 -0
  678. package/src/internal/data-grid/render/data-grid-render.header.ts +842 -0
  679. package/src/internal/data-grid/render/data-grid-render.lines.ts +379 -0
  680. package/src/internal/data-grid/render/data-grid-render.ts +825 -0
  681. package/src/internal/data-grid/render/data-grid-render.walk.ts +243 -0
  682. package/src/internal/data-grid/render/data-grid.render.rings.ts +348 -0
  683. package/src/internal/data-grid/render/draw-checkbox.ts +122 -0
  684. package/src/internal/data-grid/render/draw-edit-hover-indicator.ts +61 -0
  685. package/src/internal/data-grid/render/draw-grid-arg.ts +85 -0
  686. package/src/internal/data-grid/sprites.ts +321 -0
  687. package/src/internal/data-grid/use-animation-queue.ts +41 -0
  688. package/src/internal/data-grid/use-selection-behavior.ts +152 -0
  689. package/src/internal/data-grid-dnd/data-grid-dnd.tsx +450 -0
  690. package/src/internal/data-grid-overlay-editor/data-grid-overlay-editor-style.tsx +83 -0
  691. package/src/internal/data-grid-overlay-editor/data-grid-overlay-editor.tsx +262 -0
  692. package/src/internal/data-grid-overlay-editor/private/bubbles-overlay-editor-style.tsx +34 -0
  693. package/src/internal/data-grid-overlay-editor/private/bubbles-overlay-editor.tsx +21 -0
  694. package/src/internal/data-grid-overlay-editor/private/drilldown-overlay-editor.tsx +63 -0
  695. package/src/internal/data-grid-overlay-editor/private/image-overlay-editor-style.tsx +56 -0
  696. package/src/internal/data-grid-overlay-editor/private/image-overlay-editor.tsx +51 -0
  697. package/src/internal/data-grid-overlay-editor/private/markdown-overlay-editor-style.tsx +80 -0
  698. package/src/internal/data-grid-overlay-editor/private/markdown-overlay-editor.tsx +65 -0
  699. package/src/internal/data-grid-overlay-editor/private/number-overlay-editor-style.tsx +15 -0
  700. package/src/internal/data-grid-overlay-editor/private/number-overlay-editor.tsx +77 -0
  701. package/src/internal/data-grid-overlay-editor/private/uri-overlay-editor-style.tsx +53 -0
  702. package/src/internal/data-grid-overlay-editor/private/uri-overlay-editor.tsx +52 -0
  703. package/src/internal/data-grid-overlay-editor/use-stay-on-screen.ts +61 -0
  704. package/src/internal/data-grid-search/data-grid-search-style.tsx +96 -0
  705. package/src/internal/data-grid-search/data-grid-search.tsx +578 -0
  706. package/src/internal/growing-entry/growing-entry-style.tsx +62 -0
  707. package/src/internal/growing-entry/growing-entry.tsx +74 -0
  708. package/src/internal/markdown-div/markdown-div.tsx +55 -0
  709. package/src/internal/markdown-div/private/markdown-container.tsx +19 -0
  710. package/src/internal/scrolling-data-grid/infinite-scroller.tsx +379 -0
  711. package/src/internal/scrolling-data-grid/scrolling-data-grid.stories.tsx +164 -0
  712. package/src/internal/scrolling-data-grid/scrolling-data-grid.tsx +353 -0
  713. package/src/internal/scrolling-data-grid/use-kinetic-scroll.ts +78 -0
  714. package/src/stories/story-utils.tsx +72 -0
  715. package/test/animation-manager.test.ts +147 -0
  716. package/test/cells.test.tsx +122 -0
  717. package/test/click-outside-container.test.tsx +62 -0
  718. package/test/color-parser.test.ts +68 -0
  719. package/test/common.test.ts +74 -0
  720. package/test/compact-selection.test.ts +221 -0
  721. package/test/copy-paste.test.ts +485 -0
  722. package/test/data-editor-fns.test.ts +153 -0
  723. package/test/data-editor-input.test.tsx +683 -0
  724. package/test/data-editor-resize.test.tsx +271 -0
  725. package/test/data-editor.test.tsx +4945 -0
  726. package/test/data-grid-lib.test.ts +457 -0
  727. package/test/data-grid-overlay.test.tsx +67 -0
  728. package/test/data-grid-types.test.ts +52 -0
  729. package/test/data-grid.test.tsx +399 -0
  730. package/test/image-window-loader.test.ts +211 -0
  731. package/test/math.test.ts +201 -0
  732. package/test/render-state-provider.test.ts +86 -0
  733. package/test/row-grouping-api.test.ts +77 -0
  734. package/test/row-grouping.test.ts +586 -0
  735. package/test/test-utils.tsx +365 -0
  736. package/test/uri-cell.test.ts +124 -0
  737. package/test/use-animation-queue.test.ts +53 -0
  738. package/test/use-autoscroll.test.tsx +108 -0
  739. package/test/use-column-sizer.test.tsx +414 -0
  740. package/test/use-deep-memo.test.ts +41 -0
  741. package/test/use-kinetic-scroll.test.ts +57 -0
  742. package/test/use-rem-adjuster.test.ts +69 -0
  743. package/test/utils.test.ts +127 -0
  744. package/vitest.config.ts +40 -0
  745. package/vitest.setup.ts +13 -0
@@ -0,0 +1,4945 @@
1
+ /* eslint-disable sonarjs/no-duplicate-string */
2
+ import * as React from "react";
3
+ import { render, fireEvent, screen, act, createEvent } from "@testing-library/react";
4
+ import {
5
+ CompactSelection,
6
+ DataEditor,
7
+ type GridCell,
8
+ GridCellKind,
9
+ isSizedGridColumn,
10
+ type Item,
11
+ markerCellRenderer,
12
+ type InnerGridCell,
13
+ type InternalCellRenderer,
14
+ AllCellRenderers,
15
+ type ProvideEditorCallback,
16
+ } from "../src/index.js";
17
+ import type { CustomCell } from "../src/internal/data-grid/data-grid-types.js";
18
+ import type { DataEditorRef } from "../src/data-editor/data-editor.js";
19
+ import { assert } from "../src/common/support.js";
20
+ import { vi, type Mock, expect, describe, test, beforeEach, afterEach } from "vitest";
21
+ import type { GridKeyEventArgs } from "../src/internal/data-grid/event-args.js";
22
+ import {
23
+ EventedDataEditor,
24
+ basicProps,
25
+ prep,
26
+ sendClick,
27
+ sendTouchClick,
28
+ getCellCenterPositionForDefaultGrid,
29
+ Context,
30
+ standardBeforeEach,
31
+ standardAfterEach,
32
+ } from "./test-utils.js";
33
+
34
+ describe("data-editor", () => {
35
+ vi.mock("../src/common/resize-detector", () => {
36
+ return {
37
+ useResizeDetector: () => ({ ref: undefined, width: 1000, height: 1000 }),
38
+ };
39
+ });
40
+
41
+ beforeEach(() => {
42
+ standardBeforeEach();
43
+ });
44
+
45
+ afterEach(() => {
46
+ standardAfterEach();
47
+ });
48
+
49
+ test("Focus a11y cell", async () => {
50
+ const spy = vi.fn();
51
+ vi.useFakeTimers();
52
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
53
+ wrapper: Context,
54
+ });
55
+ prep(false);
56
+
57
+ const a11ycell = screen.getByTestId("glide-cell-0-5");
58
+ fireEvent.focus(a11ycell);
59
+
60
+ expect(spy).toBeCalledWith(
61
+ expect.objectContaining({
62
+ current: expect.objectContaining({ cell: [0, 5] }),
63
+ })
64
+ );
65
+ });
66
+
67
+ test("Click a11y cell", async () => {
68
+ vi.useFakeTimers();
69
+ render(<EventedDataEditor {...basicProps} />, {
70
+ wrapper: Context,
71
+ });
72
+ prep(false);
73
+
74
+ const a11ycell = screen.getByTestId("glide-cell-0-5");
75
+ fireEvent.click(a11ycell);
76
+ });
77
+
78
+ test("emits contextmenu for cell", async () => {
79
+ const spy = vi.fn();
80
+ const spySelection = vi.fn();
81
+
82
+ vi.useFakeTimers();
83
+ render(<DataEditor {...basicProps} onCellContextMenu={spy} onGridSelectionChange={spySelection} />, {
84
+ wrapper: Context,
85
+ });
86
+ const scroller = prep();
87
+
88
+ assert(scroller !== null);
89
+
90
+ screen.getByTestId("data-grid-canvas");
91
+ fireEvent.contextMenu(scroller, {
92
+ clientX: 300, // Col B
93
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
94
+ });
95
+
96
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
97
+ expect(spySelection).toHaveBeenCalledWith(
98
+ expect.objectContaining({
99
+ current: expect.objectContaining({ cell: [1, 1] }),
100
+ })
101
+ );
102
+ });
103
+
104
+ test("emits contextmenu for row marker", async () => {
105
+ const spy = vi.fn();
106
+ const spySelection = vi.fn();
107
+
108
+ vi.useFakeTimers();
109
+ render(
110
+ <DataEditor
111
+ {...basicProps}
112
+ rowMarkers="both"
113
+ onCellContextMenu={spy}
114
+ onGridSelectionChange={spySelection}
115
+ />,
116
+ {
117
+ wrapper: Context,
118
+ }
119
+ );
120
+ const scroller = prep();
121
+
122
+ assert(scroller !== null);
123
+
124
+ screen.getByTestId("data-grid-canvas");
125
+ fireEvent.contextMenu(scroller, {
126
+ clientX: 10, // Row marker
127
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
128
+ });
129
+
130
+ expect(spy).toHaveBeenCalledWith([-1, 1], expect.anything());
131
+ });
132
+
133
+ test("emits contextmenu for cell but does not change selection if already selected - rows", async () => {
134
+ const spy = vi.fn();
135
+ const spySelection = vi.fn();
136
+
137
+ vi.useFakeTimers();
138
+ render(
139
+ <DataEditor
140
+ {...basicProps}
141
+ gridSelection={{
142
+ columns: CompactSelection.empty(),
143
+ rows: CompactSelection.fromSingleSelection(1),
144
+ }}
145
+ onCellContextMenu={spy}
146
+ onGridSelectionChange={spySelection}
147
+ />,
148
+ {
149
+ wrapper: Context,
150
+ }
151
+ );
152
+ const scroller = prep();
153
+
154
+ assert(scroller !== null);
155
+
156
+ screen.getByTestId("data-grid-canvas");
157
+ fireEvent.contextMenu(scroller, {
158
+ clientX: 300, // Col B
159
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
160
+ });
161
+
162
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
163
+ expect(spySelection).not.toHaveBeenCalled();
164
+ });
165
+
166
+ test("emits contextmenu for cell but does not change selection if already selected - cols", async () => {
167
+ const spy = vi.fn();
168
+ const spySelection = vi.fn();
169
+
170
+ vi.useFakeTimers();
171
+ render(
172
+ <DataEditor
173
+ {...basicProps}
174
+ gridSelection={{
175
+ columns: CompactSelection.fromSingleSelection(1),
176
+ rows: CompactSelection.empty(),
177
+ }}
178
+ onCellContextMenu={spy}
179
+ onGridSelectionChange={spySelection}
180
+ />,
181
+ {
182
+ wrapper: Context,
183
+ }
184
+ );
185
+ const scroller = prep();
186
+
187
+ assert(scroller !== null);
188
+
189
+ screen.getByTestId("data-grid-canvas");
190
+ fireEvent.contextMenu(scroller, {
191
+ clientX: 300, // Col B
192
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
193
+ });
194
+
195
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
196
+ expect(spySelection).not.toHaveBeenCalled();
197
+ });
198
+
199
+ test("middle click does not change selection", async () => {
200
+ const spySelection = vi.fn();
201
+
202
+ vi.useFakeTimers();
203
+ render(
204
+ <DataEditor
205
+ {...basicProps}
206
+ gridSelection={{
207
+ columns: CompactSelection.empty(),
208
+ rows: CompactSelection.empty(),
209
+ }}
210
+ onGridSelectionChange={spySelection}
211
+ />,
212
+ {
213
+ wrapper: Context,
214
+ }
215
+ );
216
+ prep();
217
+
218
+ const canvas = screen.getByTestId("data-grid-canvas");
219
+
220
+ screen.getByTestId("data-grid-canvas");
221
+ sendClick(canvas, {
222
+ clientX: 300, // Col B
223
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
224
+ button: 1,
225
+ });
226
+
227
+ expect(spySelection).not.toHaveBeenCalled();
228
+ });
229
+
230
+ test("emits contextmenu for cell but does not change selection if already selected - current.cell", async () => {
231
+ const spy = vi.fn();
232
+ const spySelection = vi.fn();
233
+
234
+ vi.useFakeTimers();
235
+ render(
236
+ <DataEditor
237
+ {...basicProps}
238
+ gridSelection={{
239
+ columns: CompactSelection.empty(),
240
+ rows: CompactSelection.empty(),
241
+ current: {
242
+ cell: [1, 1],
243
+ range: { x: 1, y: 1, width: 1, height: 1 },
244
+ rangeStack: [],
245
+ },
246
+ }}
247
+ onCellContextMenu={spy}
248
+ onGridSelectionChange={spySelection}
249
+ />,
250
+ {
251
+ wrapper: Context,
252
+ }
253
+ );
254
+ const scroller = prep();
255
+
256
+ assert(scroller !== null);
257
+
258
+ screen.getByTestId("data-grid-canvas");
259
+ fireEvent.contextMenu(scroller, {
260
+ clientX: 300, // Col B
261
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
262
+ });
263
+
264
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
265
+ expect(spySelection).not.toHaveBeenCalled();
266
+ });
267
+
268
+ test("emits contextmenu for cell but does not change selection if already selected - current.range", async () => {
269
+ const spy = vi.fn();
270
+ const spySelection = vi.fn();
271
+
272
+ vi.useFakeTimers();
273
+ render(
274
+ <DataEditor
275
+ {...basicProps}
276
+ gridSelection={{
277
+ columns: CompactSelection.empty(),
278
+ rows: CompactSelection.empty(),
279
+ current: {
280
+ cell: [0, 0],
281
+ range: { x: 0, y: 0, width: 2, height: 2 },
282
+ rangeStack: [],
283
+ },
284
+ }}
285
+ onCellContextMenu={spy}
286
+ onGridSelectionChange={spySelection}
287
+ />,
288
+ {
289
+ wrapper: Context,
290
+ }
291
+ );
292
+ const scroller = prep();
293
+
294
+ assert(scroller !== null);
295
+
296
+ screen.getByTestId("data-grid-canvas");
297
+ fireEvent.contextMenu(scroller, {
298
+ clientX: 300, // Col B
299
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
300
+ });
301
+
302
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
303
+ expect(spySelection).not.toHaveBeenCalled();
304
+ });
305
+
306
+ test("emits contextmenu for cell row markers", async () => {
307
+ const spy = vi.fn();
308
+ const spySelection = vi.fn();
309
+
310
+ vi.useFakeTimers();
311
+ render(
312
+ <DataEditor
313
+ {...basicProps}
314
+ rowMarkers={"both"}
315
+ onCellContextMenu={spy}
316
+ onGridSelectionChange={spySelection}
317
+ />,
318
+ {
319
+ wrapper: Context,
320
+ }
321
+ );
322
+ const scroller = prep();
323
+
324
+ assert(scroller !== null);
325
+
326
+ screen.getByTestId("data-grid-canvas");
327
+ fireEvent.contextMenu(scroller, {
328
+ clientX: 320, // Col B
329
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
330
+ });
331
+
332
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
333
+ expect(spySelection).toHaveBeenCalledWith(
334
+ expect.objectContaining({
335
+ current: expect.objectContaining({ cell: [1, 1] }),
336
+ })
337
+ );
338
+ });
339
+
340
+ test("Emits cell click", async () => {
341
+ const spy = vi.fn();
342
+
343
+ vi.useFakeTimers();
344
+ render(<DataEditor {...basicProps} onCellClicked={spy} />, {
345
+ wrapper: Context,
346
+ });
347
+ prep();
348
+
349
+ const canvas = screen.getByTestId("data-grid-canvas");
350
+
351
+ sendClick(canvas, {
352
+ clientX: 300, // Col B
353
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
354
+ });
355
+
356
+ expect(spy).toHaveBeenCalled();
357
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
358
+ });
359
+
360
+ test("Emits cell clicked with middle button", async () => {
361
+ const spy = vi.fn();
362
+
363
+ vi.useFakeTimers();
364
+ render(<DataEditor {...basicProps} onCellClicked={spy} />, {
365
+ wrapper: Context,
366
+ });
367
+ prep();
368
+
369
+ const canvas = screen.getByTestId("data-grid-canvas");
370
+
371
+ sendClick(canvas, {
372
+ button: 1,
373
+ clientX: 300, // Col B
374
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
375
+ });
376
+
377
+ expect(spy).toHaveBeenCalled();
378
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
379
+ });
380
+
381
+ test("Does not emits cell clicked with back button", async () => {
382
+ const spy = vi.fn();
383
+
384
+ vi.useFakeTimers();
385
+ render(<DataEditor {...basicProps} onCellClicked={spy} />, {
386
+ wrapper: Context,
387
+ });
388
+ prep();
389
+
390
+ const canvas = screen.getByTestId("data-grid-canvas");
391
+
392
+ sendClick(canvas, {
393
+ button: 4,
394
+ clientX: 300, // Col B
395
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
396
+ });
397
+
398
+ expect(spy).not.toHaveBeenCalled();
399
+ });
400
+
401
+ test("Emits cell click with touch", async () => {
402
+ const spy = vi.fn();
403
+
404
+ vi.useFakeTimers();
405
+ render(<DataEditor {...basicProps} onCellClicked={spy} />, {
406
+ wrapper: Context,
407
+ });
408
+ prep();
409
+
410
+ const canvas = screen.getByTestId("data-grid-canvas");
411
+ sendTouchClick(canvas, {
412
+ touches: [
413
+ {
414
+ clientX: 300, // Col B
415
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
416
+ },
417
+ ],
418
+ });
419
+
420
+ expect(spy).toHaveBeenCalled();
421
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
422
+ });
423
+
424
+ test("Emits activated event on double click", async () => {
425
+ const spy = vi.fn();
426
+
427
+ vi.useFakeTimers();
428
+ render(<DataEditor {...basicProps} onCellActivated={spy} />, {
429
+ wrapper: Context,
430
+ });
431
+ prep();
432
+
433
+ const canvas = screen.getByTestId("data-grid-canvas");
434
+ sendClick(canvas, {
435
+ clientX: 300, // Col B
436
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
437
+ });
438
+
439
+ sendClick(canvas, {
440
+ clientX: 300, // Col B
441
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
442
+ });
443
+
444
+ expect(spy).toHaveBeenCalled();
445
+ const event = spy.mock.calls[0][1];
446
+ expect(event).toEqual(
447
+ expect.objectContaining({
448
+ inputType: "pointer",
449
+ pointerActivation: "double-click",
450
+ })
451
+ );
452
+ });
453
+
454
+ describe("cellActivationBehavior", () => {
455
+ test("double-click in time", async () => {
456
+ const spy = vi.fn();
457
+
458
+ vi.useFakeTimers();
459
+ render(<DataEditor {...basicProps} onCellActivated={spy} cellActivationBehavior="double-click" />, {
460
+ wrapper: Context,
461
+ });
462
+ prep(false);
463
+
464
+ const canvas = screen.getByTestId("data-grid-canvas");
465
+ sendClick(canvas, {
466
+ clientX: 300, // Col B
467
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
468
+ });
469
+
470
+ act(() => {
471
+ vi.advanceTimersByTime(400);
472
+ });
473
+
474
+ sendClick(canvas, {
475
+ clientX: 300, // Col B
476
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
477
+ });
478
+
479
+ expect(spy).toHaveBeenCalled();
480
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
481
+ });
482
+
483
+ test("double-click miss", async () => {
484
+ const spy = vi.fn();
485
+
486
+ vi.useFakeTimers();
487
+ render(<DataEditor {...basicProps} onCellActivated={spy} cellActivationBehavior="double-click" />, {
488
+ wrapper: Context,
489
+ });
490
+ prep(false);
491
+
492
+ const canvas = screen.getByTestId("data-grid-canvas");
493
+ sendClick(canvas, {
494
+ clientX: 300, // Col B
495
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
496
+ });
497
+
498
+ act(() => {
499
+ vi.advanceTimersByTime(600);
500
+ });
501
+
502
+ sendClick(canvas, {
503
+ clientX: 300, // Col B
504
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
505
+ });
506
+
507
+ expect(spy).not.toHaveBeenCalled();
508
+ });
509
+
510
+ test("second-click", async () => {
511
+ const spy = vi.fn();
512
+
513
+ vi.useFakeTimers();
514
+ render(<DataEditor {...basicProps} onCellActivated={spy} cellActivationBehavior="second-click" />, {
515
+ wrapper: Context,
516
+ });
517
+ prep(false);
518
+
519
+ const canvas = screen.getByTestId("data-grid-canvas");
520
+ sendClick(canvas, {
521
+ clientX: 300, // Col B
522
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
523
+ });
524
+
525
+ act(() => {
526
+ vi.advanceTimersByTime(1600);
527
+ });
528
+
529
+ sendClick(canvas, {
530
+ clientX: 300, // Col B
531
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
532
+ });
533
+
534
+ expect(spy).toHaveBeenCalled();
535
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
536
+ });
537
+
538
+ test("single-click", async () => {
539
+ const spy = vi.fn();
540
+
541
+ vi.useFakeTimers();
542
+ render(<DataEditor {...basicProps} onCellActivated={spy} cellActivationBehavior="single-click" />, {
543
+ wrapper: Context,
544
+ });
545
+ prep(false);
546
+
547
+ const canvas = screen.getByTestId("data-grid-canvas");
548
+ sendClick(
549
+ canvas,
550
+ {
551
+ clientX: 300, // Col B
552
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
553
+ },
554
+ true
555
+ );
556
+
557
+ expect(spy).toHaveBeenCalled();
558
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
559
+ });
560
+ });
561
+
562
+ test("Does not emit activated event on double click with different buttons", async () => {
563
+ const spy = vi.fn();
564
+
565
+ vi.useFakeTimers();
566
+ render(<DataEditor {...basicProps} onCellActivated={spy} />, {
567
+ wrapper: Context,
568
+ });
569
+ prep();
570
+
571
+ const canvas = screen.getByTestId("data-grid-canvas");
572
+ sendClick(canvas, {
573
+ button: 0,
574
+ clientX: 300, // Col B
575
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
576
+ });
577
+
578
+ sendClick(canvas, {
579
+ button: 1,
580
+ clientX: 300, // Col B
581
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
582
+ });
583
+
584
+ expect(spy).not.toHaveBeenCalled();
585
+ });
586
+
587
+ test("Emits activated event on Enter key", async () => {
588
+ const spy = vi.fn();
589
+
590
+ vi.useFakeTimers();
591
+ render(<DataEditor {...basicProps} onCellActivated={spy} />, {
592
+ wrapper: Context,
593
+ });
594
+ prep(false);
595
+
596
+ const canvas = screen.getByTestId("data-grid-canvas");
597
+ sendClick(canvas, {
598
+ clientX: 300, // Col B
599
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
600
+ });
601
+
602
+ fireEvent.keyDown(canvas, {
603
+ key: "Enter",
604
+ });
605
+
606
+ act(() => {
607
+ vi.runAllTimers();
608
+ });
609
+
610
+ expect(spy).toHaveBeenCalled();
611
+ const event = spy.mock.calls[0][1];
612
+ expect(event).toEqual(
613
+ expect.objectContaining({
614
+ inputType: "keyboard",
615
+ key: "Enter",
616
+ })
617
+ );
618
+ });
619
+
620
+ test("Toggle boolean with Enter key", async () => {
621
+ const spy = vi.fn();
622
+ const editSpy = vi.fn();
623
+
624
+ vi.useFakeTimers();
625
+ render(<DataEditor {...basicProps} onCellActivated={spy} onCellEdited={editSpy} />, {
626
+ wrapper: Context,
627
+ });
628
+ prep(false);
629
+
630
+ const canvas = screen.getByTestId("data-grid-canvas");
631
+ sendClick(canvas, {
632
+ clientX: 830, // Col Boolean
633
+ clientY: 36 * 2 + 32 + 16, // Row 2 (0 indexed)
634
+ });
635
+
636
+ fireEvent.keyDown(canvas, {
637
+ key: "Enter",
638
+ });
639
+
640
+ act(() => {
641
+ vi.runAllTimers();
642
+ });
643
+
644
+ expect(spy).toHaveBeenCalled();
645
+ expect(spy).toHaveBeenCalledWith([7, 2], expect.anything());
646
+ expect(editSpy).toHaveBeenCalledWith([7, 2], {
647
+ allowOverlay: false,
648
+ data: true,
649
+ kind: "boolean",
650
+ readonly: false,
651
+ });
652
+ });
653
+
654
+ test("Emits activated event on Space key", async () => {
655
+ const spy = vi.fn();
656
+
657
+ vi.useFakeTimers();
658
+ render(<DataEditor {...basicProps} onCellActivated={spy} />, {
659
+ wrapper: Context,
660
+ });
661
+ prep(false);
662
+
663
+ const canvas = screen.getByTestId("data-grid-canvas");
664
+ sendClick(canvas, {
665
+ clientX: 300, // Col B
666
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
667
+ });
668
+
669
+ fireEvent.keyDown(canvas, {
670
+ key: " ",
671
+ });
672
+
673
+ act(() => {
674
+ vi.runAllTimers();
675
+ });
676
+
677
+ expect(spy).toHaveBeenCalled();
678
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
679
+ });
680
+
681
+ test("Emits activated event when typing", async () => {
682
+ const spy = vi.fn();
683
+
684
+ vi.useFakeTimers();
685
+ render(<DataEditor {...basicProps} onCellActivated={spy} />, {
686
+ wrapper: Context,
687
+ });
688
+ prep(false);
689
+
690
+ const canvas = screen.getByTestId("data-grid-canvas");
691
+ sendClick(canvas, {
692
+ clientX: 300, // Col B
693
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
694
+ });
695
+
696
+ fireEvent.keyDown(canvas, {
697
+ key: "A",
698
+ });
699
+
700
+ act(() => {
701
+ vi.runAllTimers();
702
+ });
703
+
704
+ expect(spy).toHaveBeenCalled();
705
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
706
+ });
707
+
708
+ test("keyDown and keyUp events include the cell location", async () => {
709
+ let keyDownEvent: GridKeyEventArgs | undefined;
710
+ let keyUpEvent: GridKeyEventArgs | undefined;
711
+ const keyDown = (e: GridKeyEventArgs) => {
712
+ keyDownEvent = e;
713
+ };
714
+ const keyUp = (e: GridKeyEventArgs) => {
715
+ keyUpEvent = e;
716
+ };
717
+
718
+ vi.useFakeTimers();
719
+ render(<DataEditor {...basicProps} onKeyDown={keyDown} onKeyUp={keyUp} />, {
720
+ wrapper: Context,
721
+ });
722
+ prep(false);
723
+
724
+ const canvas = screen.getByTestId("data-grid-canvas");
725
+ sendClick(canvas, {
726
+ clientX: 300, // Col B
727
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
728
+ });
729
+
730
+ fireEvent.keyDown(canvas, {
731
+ key: " ",
732
+ });
733
+
734
+ fireEvent.keyUp(canvas, {
735
+ key: " ",
736
+ });
737
+
738
+ act(() => {
739
+ vi.runAllTimers();
740
+ });
741
+
742
+ expect(keyDownEvent?.location).toEqual([1, 1]);
743
+ expect(keyUpEvent?.location).toEqual([1, 1]);
744
+ });
745
+
746
+ test("Doesn't emit cell click if mouseDown happened in a different cell", async () => {
747
+ const spy = vi.fn();
748
+
749
+ vi.useFakeTimers();
750
+ render(<DataEditor {...basicProps} onCellClicked={spy} />, {
751
+ wrapper: Context,
752
+ });
753
+ prep();
754
+
755
+ const canvas = screen.getByTestId("data-grid-canvas");
756
+ fireEvent.pointerDown(canvas, {
757
+ clientX: 300, // Col B, ends at x = 310
758
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
759
+ });
760
+
761
+ fireEvent.pointerUp(canvas, {
762
+ clientX: 320, // Col C, started at x = 310
763
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
764
+ });
765
+
766
+ expect(spy).not.toHaveBeenCalled();
767
+ });
768
+
769
+ test("Doesn't emit header click if mouseDown happened in a different cell", async () => {
770
+ const spy = vi.fn();
771
+
772
+ vi.useFakeTimers();
773
+ render(<DataEditor {...basicProps} onHeaderClicked={spy} />, {
774
+ wrapper: Context,
775
+ });
776
+ prep();
777
+
778
+ const canvas = screen.getByTestId("data-grid-canvas");
779
+ fireEvent.pointerDown(canvas, {
780
+ clientX: 300, // Col B, ends at x = 310
781
+ clientY: 16, // Header
782
+ });
783
+
784
+ fireEvent.pointerUp(canvas, {
785
+ clientX: 320, // Col C, started at x = 310
786
+ clientY: 16, // Header
787
+ });
788
+
789
+ expect(spy).not.toHaveBeenCalled();
790
+ });
791
+
792
+ test("Uneven rows cell click", async () => {
793
+ const spy = vi.fn();
794
+
795
+ vi.useFakeTimers();
796
+ render(<DataEditor {...basicProps} onCellClicked={spy} rowHeight={r => (r % 2 === 0 ? 32 : 64)} />, {
797
+ wrapper: Context,
798
+ });
799
+ prep();
800
+
801
+ const canvas = screen.getByTestId("data-grid-canvas");
802
+ sendClick(canvas, {
803
+ clientX: 300, // Col B
804
+ clientY: 36 + 64 + 16, // Row 1 (0 indexed)
805
+ });
806
+
807
+ expect(spy).toHaveBeenCalled();
808
+ expect(spy).toHaveBeenCalledWith([1, 1], expect.anything());
809
+ });
810
+
811
+ test("Emits finished editing", async () => {
812
+ const spy = vi.fn();
813
+ vi.useFakeTimers();
814
+ render(<DataEditor {...basicProps} onFinishedEditing={spy} />, {
815
+ wrapper: Context,
816
+ });
817
+ prep();
818
+ const canvas = screen.getByTestId("data-grid-canvas");
819
+ sendClick(canvas, {
820
+ clientX: 300, // Col B
821
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
822
+ });
823
+
824
+ fireEvent.keyDown(canvas, {
825
+ keyCode: 74,
826
+ key: "j",
827
+ });
828
+
829
+ await act(() => new Promise(r => window.setTimeout(r, 500)));
830
+
831
+ const overlay = screen.getByDisplayValue("j");
832
+
833
+ vi.useFakeTimers();
834
+ fireEvent.keyDown(overlay, {
835
+ key: "Enter",
836
+ });
837
+
838
+ act(() => {
839
+ vi.runAllTimers();
840
+ });
841
+
842
+ expect(spy).toBeCalledWith(
843
+ { allowOverlay: true, allowWrapping: true, data: "j", displayData: "1, 1", kind: "text" },
844
+ [0, 1]
845
+ );
846
+ });
847
+
848
+ test("Does not edit when validation fails", async () => {
849
+ const spy = vi.fn();
850
+ vi.useFakeTimers();
851
+ render(<DataEditor {...basicProps} onCellEdited={spy} validateCell={() => false} />, {
852
+ wrapper: Context,
853
+ });
854
+ prep();
855
+ const canvas = screen.getByTestId("data-grid-canvas");
856
+ sendClick(canvas, {
857
+ clientX: 300, // Col B
858
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
859
+ });
860
+
861
+ fireEvent.keyDown(canvas, {
862
+ keyCode: 74,
863
+ key: "j",
864
+ });
865
+
866
+ await act(() => new Promise(r => window.setTimeout(r, 500)));
867
+
868
+ const overlay = screen.getByDisplayValue("j");
869
+
870
+ vi.useFakeTimers();
871
+ fireEvent.keyDown(overlay, {
872
+ key: "Enter",
873
+ });
874
+
875
+ act(() => {
876
+ vi.runAllTimers();
877
+ });
878
+
879
+ expect(spy).not.toBeCalled();
880
+ });
881
+
882
+ test("Emits header click", async () => {
883
+ const spy = vi.fn();
884
+
885
+ vi.useFakeTimers();
886
+ render(<DataEditor {...basicProps} onHeaderClicked={spy} />, {
887
+ wrapper: Context,
888
+ });
889
+ prep();
890
+
891
+ const canvas = screen.getByTestId("data-grid-canvas");
892
+ sendClick(canvas, {
893
+ clientX: 300, // Col B
894
+ clientY: 16, // Header
895
+ });
896
+
897
+ expect(spy).toHaveBeenCalled();
898
+ expect(spy).toHaveBeenCalledWith(1, expect.anything());
899
+ });
900
+
901
+ test("Emits header click on touch", async () => {
902
+ const spy = vi.fn();
903
+
904
+ vi.useFakeTimers();
905
+ render(<DataEditor {...basicProps} onHeaderClicked={spy} />, {
906
+ wrapper: Context,
907
+ });
908
+ prep();
909
+
910
+ const canvas = screen.getByTestId("data-grid-canvas");
911
+ sendTouchClick(canvas, {
912
+ touches: [
913
+ {
914
+ clientX: 300, // Col B
915
+ clientY: 16, // Header
916
+ },
917
+ ],
918
+ });
919
+
920
+ expect(spy).toHaveBeenCalled();
921
+ expect(spy).toHaveBeenCalledWith(1, expect.anything());
922
+ });
923
+
924
+ test("Does emit header click on row marker column", async () => {
925
+ const spy = vi.fn();
926
+
927
+ vi.useFakeTimers();
928
+ render(<DataEditor {...basicProps} rowMarkers="both" onHeaderClicked={spy} />, {
929
+ wrapper: Context,
930
+ });
931
+ prep();
932
+
933
+ const canvas = screen.getByTestId("data-grid-canvas");
934
+ sendClick(canvas, {
935
+ clientX: 10, // Col B
936
+ clientY: 16, // Header
937
+ });
938
+
939
+ expect(spy).not.toHaveBeenCalled();
940
+ });
941
+
942
+ test("Group header sections", async () => {
943
+ const spy = vi.fn();
944
+
945
+ vi.useFakeTimers();
946
+ render(
947
+ <EventedDataEditor
948
+ {...basicProps}
949
+ getGroupDetails={g => ({
950
+ name: g,
951
+ icon: "headerCode",
952
+ })}
953
+ columns={basicProps.columns.map(c => ({ ...c, group: "A" }))}
954
+ onGridSelectionChange={spy}
955
+ />,
956
+ {
957
+ wrapper: Context,
958
+ }
959
+ );
960
+ prep();
961
+
962
+ const canvas = screen.getByTestId("data-grid-canvas");
963
+ sendClick(canvas, {
964
+ clientX: 300, // Col B
965
+ clientY: 16, // GroupHeader
966
+ });
967
+
968
+ expect(spy).toHaveBeenCalledTimes(1);
969
+ expect(spy).toHaveBeenCalledWith({
970
+ columns: CompactSelection.fromSingleSelection([0, 11]),
971
+ rows: CompactSelection.empty(),
972
+ });
973
+
974
+ spy.mockClear();
975
+
976
+ sendClick(canvas, {
977
+ ctrlKey: true,
978
+ clientX: 300, // Col B
979
+ clientY: 16, // GroupHeader
980
+ });
981
+
982
+ expect(spy).toHaveBeenCalled();
983
+ expect(spy).toHaveBeenCalledWith({
984
+ columns: CompactSelection.empty(),
985
+ rows: CompactSelection.empty(),
986
+ });
987
+
988
+ spy.mockClear();
989
+
990
+ sendClick(canvas, {
991
+ ctrlKey: true,
992
+ clientX: 300, // Col B
993
+ clientY: 16, // GroupHeader
994
+ });
995
+
996
+ expect(spy).toHaveBeenCalled();
997
+ expect(spy).toHaveBeenCalledWith({
998
+ rows: CompactSelection.empty(),
999
+ current: undefined,
1000
+ columns: CompactSelection.fromSingleSelection([0, 11]),
1001
+ });
1002
+ });
1003
+
1004
+ test("Rename group header shows", async () => {
1005
+ const spy = vi.fn();
1006
+
1007
+ vi.useFakeTimers();
1008
+ render(
1009
+ <DataEditor
1010
+ {...basicProps}
1011
+ columns={basicProps.columns.map(c => ({ ...c, group: c.title }))}
1012
+ onGroupHeaderRenamed={spy}
1013
+ />,
1014
+ {
1015
+ wrapper: Context,
1016
+ }
1017
+ );
1018
+ prep();
1019
+
1020
+ const canvas = screen.getByTestId("data-grid-canvas");
1021
+ fireEvent.pointerMove(canvas, {
1022
+ clientX: 300, // Col B
1023
+ clientY: 16, // Group Header
1024
+ });
1025
+
1026
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
1027
+
1028
+ sendClick(canvas, {
1029
+ clientX: 300, // Col B
1030
+ clientY: 16, // Group Header
1031
+ });
1032
+
1033
+ expect(spy).not.toHaveBeenCalled();
1034
+ const groupInput = screen.getByTestId("group-rename-input");
1035
+ expect(document.body.contains(groupInput)).toBe(true);
1036
+
1037
+ fireEvent.change(groupInput, {
1038
+ target: {
1039
+ value: "Test",
1040
+ },
1041
+ });
1042
+
1043
+ fireEvent.keyDown(groupInput, {
1044
+ key: "Enter",
1045
+ });
1046
+
1047
+ expect(spy).toHaveBeenCalledWith("B", "Test");
1048
+ });
1049
+
1050
+ test("Emits header menu click", async () => {
1051
+ const spy = vi.fn();
1052
+
1053
+ vi.useFakeTimers();
1054
+ render(
1055
+ <DataEditor
1056
+ {...basicProps}
1057
+ columns={basicProps.columns.map(c => ({ ...c, hasMenu: true }))}
1058
+ onHeaderMenuClick={spy}
1059
+ />,
1060
+ {
1061
+ wrapper: Context,
1062
+ }
1063
+ );
1064
+ prep();
1065
+
1066
+ const canvas = screen.getByTestId("data-grid-canvas");
1067
+ fireEvent.pointerMove(canvas, {
1068
+ clientX: 300, // Col B
1069
+ clientY: 16, // Header
1070
+ });
1071
+
1072
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
1073
+
1074
+ sendClick(canvas, {
1075
+ clientX: 300, // Col B
1076
+ clientY: 16, // Header
1077
+ });
1078
+
1079
+ expect(spy).toHaveBeenCalled();
1080
+ expect(spy).toHaveBeenCalledWith(1, expect.anything());
1081
+ });
1082
+
1083
+ test("Emits group header clicked on touch", async () => {
1084
+ const spy = vi.fn();
1085
+
1086
+ vi.useFakeTimers();
1087
+ render(
1088
+ <DataEditor
1089
+ {...basicProps}
1090
+ columns={basicProps.columns.map(c => ({ ...c, group: "Main" }))}
1091
+ rowMarkers="both"
1092
+ onGroupHeaderClicked={spy}
1093
+ />,
1094
+ {
1095
+ wrapper: Context,
1096
+ }
1097
+ );
1098
+ prep();
1099
+
1100
+ const canvas = screen.getByTestId("data-grid-canvas");
1101
+ sendTouchClick(canvas, {
1102
+ touches: [
1103
+ {
1104
+ clientX: 300, // Col B
1105
+ clientY: 16, // Group header
1106
+ },
1107
+ ],
1108
+ });
1109
+
1110
+ expect(spy).toHaveBeenCalled();
1111
+ expect(spy).toHaveBeenCalledWith(1, expect.objectContaining({ location: [2, -2] }));
1112
+ });
1113
+
1114
+ test("Emits item hover on correct location", async () => {
1115
+ const spy = vi.fn();
1116
+
1117
+ vi.useFakeTimers();
1118
+ render(<DataEditor {...basicProps} rowMarkers="both" onItemHovered={spy} />, {
1119
+ wrapper: Context,
1120
+ });
1121
+ prep();
1122
+
1123
+ const canvas = screen.getByTestId("data-grid-canvas");
1124
+ fireEvent.pointerMove(canvas, {
1125
+ clientX: 300, // Col B
1126
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1127
+ });
1128
+
1129
+ expect(spy).toHaveBeenCalled();
1130
+ expect(spy).toHaveBeenCalledWith(expect.objectContaining({ location: [1, 1] }));
1131
+ });
1132
+
1133
+ test("Emits mouse move on correct location", async () => {
1134
+ const spy = vi.fn();
1135
+
1136
+ vi.useFakeTimers();
1137
+ render(<DataEditor {...basicProps} rowMarkers="both" onMouseMove={spy} />, {
1138
+ wrapper: Context,
1139
+ });
1140
+ prep();
1141
+
1142
+ const canvas = screen.getByTestId("data-grid-canvas");
1143
+ fireEvent.pointerMove(canvas, {
1144
+ clientX: 300, // Col B
1145
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1146
+ });
1147
+
1148
+ expect(spy).toHaveBeenCalled();
1149
+ expect(spy).toHaveBeenCalledWith(expect.objectContaining({ location: [1, 1] }));
1150
+ });
1151
+
1152
+ test("Delete cell", async () => {
1153
+ const spy = vi.fn();
1154
+
1155
+ vi.useFakeTimers();
1156
+ render(
1157
+ <DataEditor
1158
+ {...basicProps}
1159
+ onDelete={spy}
1160
+ gridSelection={{
1161
+ columns: CompactSelection.empty(),
1162
+ rows: CompactSelection.empty(),
1163
+ current: {
1164
+ cell: [2, 2],
1165
+ range: {
1166
+ x: 2,
1167
+ y: 2,
1168
+ height: 1,
1169
+ width: 1,
1170
+ },
1171
+ rangeStack: [],
1172
+ },
1173
+ }}
1174
+ rowMarkers="both"
1175
+ />,
1176
+ {
1177
+ wrapper: Context,
1178
+ }
1179
+ );
1180
+ prep();
1181
+
1182
+ const canvas = screen.getByTestId("data-grid-canvas");
1183
+ fireEvent.keyDown(canvas, {
1184
+ key: "Delete",
1185
+ });
1186
+
1187
+ expect(spy).toHaveBeenCalledWith({
1188
+ columns: CompactSelection.empty(),
1189
+ rows: CompactSelection.empty(),
1190
+ current: {
1191
+ cell: [2, 2],
1192
+ range: {
1193
+ x: 2,
1194
+ y: 2,
1195
+ height: 1,
1196
+ width: 1,
1197
+ },
1198
+ rangeStack: [],
1199
+ },
1200
+ });
1201
+ });
1202
+
1203
+ test("Delete cell callback result", async () => {
1204
+ const spy = vi.fn();
1205
+
1206
+ vi.useFakeTimers();
1207
+ render(
1208
+ <DataEditor
1209
+ {...basicProps}
1210
+ onDelete={sel => sel}
1211
+ onCellEdited={spy}
1212
+ gridSelection={{
1213
+ columns: CompactSelection.empty(),
1214
+ rows: CompactSelection.empty(),
1215
+ current: {
1216
+ cell: [2, 2],
1217
+ range: {
1218
+ x: 2,
1219
+ y: 2,
1220
+ height: 1,
1221
+ width: 1,
1222
+ },
1223
+ rangeStack: [],
1224
+ },
1225
+ }}
1226
+ rowMarkers="both"
1227
+ />,
1228
+ {
1229
+ wrapper: Context,
1230
+ }
1231
+ );
1232
+ prep();
1233
+
1234
+ const canvas = screen.getByTestId("data-grid-canvas");
1235
+ fireEvent.keyDown(canvas, {
1236
+ key: "Delete",
1237
+ });
1238
+
1239
+ expect(spy).toHaveBeenCalledWith([2, 2], expect.anything());
1240
+ });
1241
+
1242
+ test("Delete custom", async () => {
1243
+ const spy = vi.fn();
1244
+
1245
+ vi.useFakeTimers();
1246
+ render(
1247
+ <DataEditor
1248
+ {...basicProps}
1249
+ getCellContent={() => ({
1250
+ kind: GridCellKind.Custom,
1251
+ allowOverlay: true,
1252
+ copyData: "fake",
1253
+ data: "fake",
1254
+ })}
1255
+ customRenderers={[
1256
+ {
1257
+ draw: () => undefined,
1258
+ isMatch: (c): c is CustomCell => c.kind === GridCellKind.Custom,
1259
+ kind: GridCellKind.Custom,
1260
+ onDelete: spy,
1261
+ },
1262
+ ]}
1263
+ onDelete={sel => sel}
1264
+ gridSelection={{
1265
+ columns: CompactSelection.empty(),
1266
+ rows: CompactSelection.empty(),
1267
+ current: {
1268
+ cell: [2, 2],
1269
+ range: {
1270
+ x: 2,
1271
+ y: 2,
1272
+ height: 1,
1273
+ width: 1,
1274
+ },
1275
+ rangeStack: [],
1276
+ },
1277
+ }}
1278
+ rowMarkers="both"
1279
+ />,
1280
+ {
1281
+ wrapper: Context,
1282
+ }
1283
+ );
1284
+ prep();
1285
+
1286
+ const canvas = screen.getByTestId("data-grid-canvas");
1287
+ fireEvent.keyDown(canvas, {
1288
+ key: "Delete",
1289
+ });
1290
+
1291
+ expect(spy).toHaveBeenCalledWith({ allowOverlay: true, copyData: "fake", data: "fake", kind: "custom" });
1292
+ });
1293
+
1294
+ test("Delete row", async () => {
1295
+ const spy = vi.fn();
1296
+
1297
+ vi.useFakeTimers();
1298
+ render(
1299
+ <DataEditor
1300
+ {...basicProps}
1301
+ onDelete={spy}
1302
+ gridSelection={{
1303
+ columns: CompactSelection.empty(),
1304
+ rows: CompactSelection.fromSingleSelection(2),
1305
+ current: undefined,
1306
+ }}
1307
+ rowMarkers="both"
1308
+ />,
1309
+ {
1310
+ wrapper: Context,
1311
+ }
1312
+ );
1313
+ prep();
1314
+
1315
+ const canvas = screen.getByTestId("data-grid-canvas");
1316
+ fireEvent.keyDown(canvas, {
1317
+ key: "Delete",
1318
+ });
1319
+
1320
+ expect(spy).toHaveBeenCalled();
1321
+ });
1322
+
1323
+ test("Delete range", async () => {
1324
+ const spy = vi.fn();
1325
+
1326
+ vi.useFakeTimers();
1327
+ render(
1328
+ <DataEditor
1329
+ {...basicProps}
1330
+ onDelete={spy}
1331
+ gridSelection={{
1332
+ columns: CompactSelection.empty(),
1333
+ rows: CompactSelection.empty(),
1334
+ current: {
1335
+ cell: [2, 2],
1336
+ range: { x: 2, y: 2, width: 4, height: 10 },
1337
+ rangeStack: [],
1338
+ },
1339
+ }}
1340
+ rowMarkers="both"
1341
+ />,
1342
+ {
1343
+ wrapper: Context,
1344
+ }
1345
+ );
1346
+ prep();
1347
+
1348
+ const canvas = screen.getByTestId("data-grid-canvas");
1349
+ fireEvent.keyDown(canvas, {
1350
+ key: "Delete",
1351
+ });
1352
+
1353
+ expect(spy).toHaveBeenCalledWith({
1354
+ columns: CompactSelection.empty(),
1355
+ rows: CompactSelection.empty(),
1356
+ current: {
1357
+ cell: [2, 2],
1358
+ range: { x: 2, y: 2, width: 4, height: 10 },
1359
+ rangeStack: [],
1360
+ },
1361
+ });
1362
+ });
1363
+
1364
+ test("Open and close overlay", async () => {
1365
+ vi.useFakeTimers();
1366
+ render(<DataEditor {...basicProps} />, {
1367
+ wrapper: Context,
1368
+ });
1369
+ prep();
1370
+
1371
+ const canvas = screen.getByTestId("data-grid-canvas");
1372
+ sendClick(canvas, {
1373
+ clientX: 300, // Col B
1374
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1375
+ });
1376
+
1377
+ sendClick(canvas, {
1378
+ clientX: 300, // Col B
1379
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1380
+ });
1381
+
1382
+ await act(() => new Promise(r => window.setTimeout(r, 500)));
1383
+
1384
+ const overlay = screen.getByDisplayValue("Data: 1, 1");
1385
+ expect(document.body.contains(overlay)).toBe(true);
1386
+
1387
+ vi.useFakeTimers();
1388
+ fireEvent.keyDown(canvas, {
1389
+ key: "Escape",
1390
+ });
1391
+
1392
+ act(() => {
1393
+ vi.runAllTimers();
1394
+ });
1395
+
1396
+ expect(document.body.contains(overlay)).toBe(false);
1397
+ });
1398
+
1399
+ test("Open markdown overlay", async () => {
1400
+ vi.useFakeTimers();
1401
+ render(<DataEditor {...basicProps} />, {
1402
+ wrapper: Context,
1403
+ });
1404
+ prep();
1405
+
1406
+ const canvas = screen.getByTestId("data-grid-canvas");
1407
+ sendClick(canvas, {
1408
+ clientX: 980, // Col B
1409
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1410
+ });
1411
+
1412
+ sendClick(canvas, {
1413
+ clientX: 980, // Col B
1414
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1415
+ });
1416
+
1417
+ const overlay = screen.getByText("Header: 9, 1");
1418
+ expect(document.body.contains(overlay)).toBe(true);
1419
+
1420
+ vi.useFakeTimers();
1421
+ fireEvent.keyDown(canvas, {
1422
+ key: "Escape",
1423
+ });
1424
+ act(() => {
1425
+ vi.runAllTimers();
1426
+ });
1427
+
1428
+ expect(document.body.contains(overlay)).toBe(false);
1429
+ });
1430
+
1431
+ test("Open overlay with keypress", async () => {
1432
+ vi.useFakeTimers();
1433
+ render(<DataEditor {...basicProps} />, {
1434
+ wrapper: Context,
1435
+ });
1436
+ prep();
1437
+
1438
+ const canvas = screen.getByTestId("data-grid-canvas");
1439
+ sendClick(canvas, {
1440
+ clientX: 300, // Col B
1441
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1442
+ });
1443
+
1444
+ const testKeys = [
1445
+ {
1446
+ keyCode: 74,
1447
+ key: "j",
1448
+ },
1449
+ {
1450
+ keyCode: 381,
1451
+ key: "ž",
1452
+ },
1453
+ {
1454
+ keyCode: 246,
1455
+ key: "ö",
1456
+ },
1457
+ {
1458
+ keyCode: 1096,
1459
+ key: "ш",
1460
+ },
1461
+ {
1462
+ keyCode: 187,
1463
+ key: "+",
1464
+ },
1465
+ {
1466
+ keyCode: 222,
1467
+ key: "'",
1468
+ },
1469
+ ];
1470
+
1471
+ for (const key of testKeys) {
1472
+ fireEvent.keyDown(canvas, key);
1473
+ fireEvent.keyUp(canvas, key);
1474
+
1475
+ const overlay = screen.getByDisplayValue(key.key);
1476
+ expect(document.body.contains(overlay)).toBe(true);
1477
+
1478
+ vi.useFakeTimers();
1479
+ fireEvent.keyDown(overlay, {
1480
+ key: "Escape",
1481
+ });
1482
+
1483
+ act(() => {
1484
+ vi.runAllTimers();
1485
+ });
1486
+
1487
+ expect(document.body.contains(overlay)).toBe(false);
1488
+ }
1489
+ });
1490
+
1491
+ test("Open overlay with keypress when prior is disabled", async () => {
1492
+ vi.useFakeTimers();
1493
+ render(
1494
+ <DataEditor
1495
+ {...basicProps}
1496
+ getCellContent={cell => {
1497
+ const r = basicProps.getCellContent(cell);
1498
+
1499
+ if (cell[0] === 1 && cell[1] === 0)
1500
+ return {
1501
+ ...r,
1502
+ allowOverlay: false,
1503
+ readonly: true,
1504
+ };
1505
+
1506
+ return r;
1507
+ }}
1508
+ />,
1509
+ {
1510
+ wrapper: Context,
1511
+ }
1512
+ );
1513
+ prep();
1514
+
1515
+ const canvas = screen.getByTestId("data-grid-canvas");
1516
+ sendClick(canvas, {
1517
+ clientX: 300, // Col B
1518
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1519
+ });
1520
+
1521
+ fireEvent.keyDown(canvas, {
1522
+ keyCode: 74,
1523
+ key: "j",
1524
+ });
1525
+
1526
+ fireEvent.keyUp(canvas, {
1527
+ keyCode: 74,
1528
+ key: "j",
1529
+ });
1530
+
1531
+ const overlay = screen.getByDisplayValue("j");
1532
+ expect(document.body.contains(overlay)).toBe(true);
1533
+
1534
+ vi.useFakeTimers();
1535
+ fireEvent.keyDown(overlay, {
1536
+ key: "Escape",
1537
+ });
1538
+
1539
+ act(() => {
1540
+ vi.runAllTimers();
1541
+ });
1542
+
1543
+ expect(document.body.contains(overlay)).toBe(false);
1544
+ });
1545
+
1546
+ test("Editor provider receives activation info", async () => {
1547
+ const spy = vi.fn();
1548
+ const provider: ProvideEditorCallback<GridCell> = cell => {
1549
+ spy(cell.activation);
1550
+ return undefined;
1551
+ };
1552
+
1553
+ vi.useFakeTimers();
1554
+ render(<DataEditor {...basicProps} provideEditor={provider} />, { wrapper: Context });
1555
+ prep();
1556
+
1557
+ const canvas = screen.getByTestId("data-grid-canvas");
1558
+ sendClick(canvas, {
1559
+ clientX: 300,
1560
+ clientY: 36 + 32 + 16,
1561
+ });
1562
+
1563
+ sendClick(canvas, {
1564
+ clientX: 300,
1565
+ clientY: 36 + 32 + 16,
1566
+ });
1567
+
1568
+ expect(spy).toHaveBeenCalledWith(
1569
+ expect.objectContaining({
1570
+ inputType: "pointer",
1571
+ pointerActivation: "double-click",
1572
+ })
1573
+ );
1574
+ });
1575
+
1576
+ test("Send edit", async () => {
1577
+ const spy = vi.fn();
1578
+ vi.useFakeTimers();
1579
+ render(<DataEditor {...basicProps} onCellEdited={spy} />, {
1580
+ wrapper: Context,
1581
+ });
1582
+ prep(false);
1583
+
1584
+ const canvas = screen.getByTestId("data-grid-canvas");
1585
+ sendClick(canvas, {
1586
+ clientX: 300, // Col B
1587
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1588
+ });
1589
+
1590
+ act(() => {
1591
+ vi.runAllTimers();
1592
+ });
1593
+
1594
+ fireEvent.keyDown(canvas, {
1595
+ keyCode: 74,
1596
+ key: "j",
1597
+ });
1598
+
1599
+ fireEvent.keyUp(canvas, {
1600
+ keyCode: 74,
1601
+ key: "j",
1602
+ });
1603
+
1604
+ act(() => {
1605
+ vi.runAllTimers();
1606
+ });
1607
+
1608
+ const overlay = screen.getByDisplayValue("j");
1609
+ expect(document.body.contains(overlay)).toBe(true);
1610
+
1611
+ fireEvent.keyDown(overlay, {
1612
+ key: "Enter",
1613
+ });
1614
+
1615
+ act(() => {
1616
+ vi.runAllTimers();
1617
+ });
1618
+
1619
+ expect(spy).toBeCalledWith([1, 1], expect.objectContaining({ data: "j" }));
1620
+ expect(document.body.contains(overlay)).toBe(false);
1621
+ });
1622
+
1623
+ test("Send edit with click off", async () => {
1624
+ const spy = vi.fn();
1625
+ vi.useFakeTimers();
1626
+ render(<DataEditor {...basicProps} onCellEdited={spy} />, {
1627
+ wrapper: Context,
1628
+ });
1629
+ prep(false);
1630
+
1631
+ const canvas = screen.getByTestId("data-grid-canvas");
1632
+ sendClick(canvas, {
1633
+ clientX: 300, // Col B
1634
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1635
+ });
1636
+
1637
+ act(() => {
1638
+ vi.runAllTimers();
1639
+ });
1640
+
1641
+ fireEvent.keyDown(canvas, {
1642
+ keyCode: 74,
1643
+ key: "j",
1644
+ });
1645
+
1646
+ fireEvent.keyUp(canvas, {
1647
+ keyCode: 74,
1648
+ key: "j",
1649
+ });
1650
+
1651
+ act(() => {
1652
+ vi.runAllTimers();
1653
+ });
1654
+
1655
+ const overlay = screen.getByDisplayValue("j");
1656
+ expect(document.body.contains(overlay)).toBe(true);
1657
+
1658
+ sendClick(canvas, {
1659
+ clientX: 300, // Col B
1660
+ clientY: 36 + 32 * 5 + 16, // Row 1 (0 indexed)
1661
+ });
1662
+
1663
+ act(() => {
1664
+ vi.runAllTimers();
1665
+ });
1666
+
1667
+ expect(spy).toBeCalledWith([1, 1], expect.objectContaining({ data: "j" }));
1668
+ expect(document.body.contains(overlay)).toBe(false);
1669
+ });
1670
+
1671
+ test("Send edit with touch off", async () => {
1672
+ const spy = vi.fn();
1673
+ vi.useFakeTimers();
1674
+ render(<DataEditor {...basicProps} onCellEdited={spy} />, {
1675
+ wrapper: Context,
1676
+ });
1677
+ prep(false);
1678
+
1679
+ const canvas = screen.getByTestId("data-grid-canvas");
1680
+ sendClick(canvas, {
1681
+ clientX: 300, // Col B
1682
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1683
+ });
1684
+
1685
+ act(() => {
1686
+ vi.runAllTimers();
1687
+ });
1688
+
1689
+ fireEvent.keyDown(canvas, {
1690
+ keyCode: 74,
1691
+ key: "j",
1692
+ });
1693
+
1694
+ fireEvent.keyUp(canvas, {
1695
+ keyCode: 74,
1696
+ key: "j",
1697
+ });
1698
+
1699
+ act(() => {
1700
+ vi.runAllTimers();
1701
+ });
1702
+
1703
+ const overlay = screen.getByDisplayValue("j");
1704
+ expect(document.body.contains(overlay)).toBe(true);
1705
+
1706
+ sendTouchClick(canvas, {
1707
+ touches: [
1708
+ {
1709
+ clientX: 300, // Col B
1710
+ clientY: 36 + 32 * 5 + 16, // Row 1 (0 indexed)}
1711
+ },
1712
+ ],
1713
+ });
1714
+
1715
+ act(() => {
1716
+ vi.runAllTimers();
1717
+ });
1718
+
1719
+ expect(spy).toBeCalledWith([1, 1], expect.objectContaining({ data: "j" }));
1720
+ expect(document.body.contains(overlay)).toBe(false);
1721
+ });
1722
+
1723
+ test("Directly toggle booleans", async () => {
1724
+ const spy = vi.fn();
1725
+ vi.useFakeTimers();
1726
+ const ref = React.createRef<DataEditorRef>();
1727
+ render(<DataEditor {...basicProps} onCellEdited={spy} ref={ref} />, {
1728
+ wrapper: Context,
1729
+ });
1730
+ prep(false);
1731
+
1732
+ const canvas = screen.getByTestId("data-grid-canvas");
1733
+
1734
+ // We need to be focused on the grid for booleans to toggle automatically
1735
+ act(() => {
1736
+ ref.current?.focus();
1737
+ });
1738
+ act(() => {
1739
+ vi.runAllTimers();
1740
+ });
1741
+ vi.useRealTimers();
1742
+
1743
+ // [7, 0] is a checked boolean
1744
+ const [checkedX, checkedY] = getCellCenterPositionForDefaultGrid([7, 0]);
1745
+
1746
+ sendClick(canvas, { clientX: checkedX, clientY: checkedY });
1747
+
1748
+ expect(spy).toBeCalledWith([7, 0], expect.objectContaining({ data: false }));
1749
+
1750
+ // [7, 1] is an unchecked boolean
1751
+ const [uncheckedX, uncheckedY] = getCellCenterPositionForDefaultGrid([7, 1]);
1752
+
1753
+ sendClick(canvas, { clientX: uncheckedX, clientY: uncheckedY });
1754
+
1755
+ expect(spy).toBeCalledWith([7, 1], expect.objectContaining({ data: true }));
1756
+
1757
+ // [7, 2] is an indeterminate boolean
1758
+ const [indeterminateX, indeterminateY] = getCellCenterPositionForDefaultGrid([7, 2]);
1759
+
1760
+ sendClick(canvas, { clientX: indeterminateX, clientY: indeterminateY });
1761
+
1762
+ expect(spy).toBeCalledWith([7, 2], expect.objectContaining({ data: true }));
1763
+
1764
+ // [7, 3] is an empty boolean
1765
+ const [emptyX, emptyY] = getCellCenterPositionForDefaultGrid([7, 3]);
1766
+
1767
+ sendClick(canvas, { clientX: emptyX, clientY: emptyY });
1768
+
1769
+ expect(spy).toBeCalledWith([7, 3], expect.objectContaining({ data: true }));
1770
+ });
1771
+
1772
+ test("Directly toggle readonly booleans", async () => {
1773
+ const spy = vi.fn();
1774
+ vi.useFakeTimers();
1775
+ const ref = React.createRef<DataEditorRef>();
1776
+ render(<DataEditor {...basicProps} onCellEdited={spy} ref={ref} />, {
1777
+ wrapper: Context,
1778
+ });
1779
+ prep(false);
1780
+
1781
+ const canvas = screen.getByTestId("data-grid-canvas");
1782
+
1783
+ // We need to be focused on the grid for booleans to toggle automatically
1784
+ act(() => {
1785
+ ref.current?.focus();
1786
+ });
1787
+ act(() => {
1788
+ vi.runAllTimers();
1789
+ });
1790
+ vi.useRealTimers();
1791
+
1792
+ // [7, 0] is a checked boolean readonly
1793
+ const [checkedX, checkedY] = getCellCenterPositionForDefaultGrid([7, 5]);
1794
+
1795
+ sendClick(canvas, { clientX: checkedX, clientY: checkedY });
1796
+
1797
+ expect(spy).not.toBeCalled();
1798
+ });
1799
+
1800
+ test("Toggle readonly boolean with space", async () => {
1801
+ const spy = vi.fn();
1802
+ vi.useFakeTimers();
1803
+ const ref = React.createRef<DataEditorRef>();
1804
+ render(<DataEditor {...basicProps} onCellEdited={spy} ref={ref} />, {
1805
+ wrapper: Context,
1806
+ });
1807
+ prep(false);
1808
+
1809
+ const canvas = screen.getByTestId("data-grid-canvas");
1810
+
1811
+ // We need to be focused on the grid for booleans to toggle automatically
1812
+ act(() => {
1813
+ ref.current?.focus();
1814
+ });
1815
+ act(() => {
1816
+ vi.runAllTimers();
1817
+ });
1818
+
1819
+ // [7, 0] is a checked boolean readonly
1820
+ const [checkedX, checkedY] = getCellCenterPositionForDefaultGrid([7, 5]);
1821
+
1822
+ sendClick(canvas, { clientX: checkedX + 20, clientY: checkedY });
1823
+
1824
+ act(() => {
1825
+ vi.runAllTimers();
1826
+ });
1827
+
1828
+ fireEvent.keyDown(canvas, {
1829
+ key: " ",
1830
+ });
1831
+
1832
+ expect(spy).not.toBeCalled();
1833
+ });
1834
+
1835
+ test("Ref getBounds", async () => {
1836
+ const spy = vi.fn();
1837
+ vi.useFakeTimers();
1838
+ const ref = React.createRef<DataEditorRef>();
1839
+ render(<DataEditor {...basicProps} onCellEdited={spy} ref={ref} rowMarkers="both" />, {
1840
+ wrapper: Context,
1841
+ });
1842
+ prep(false);
1843
+
1844
+ act(() => {
1845
+ vi.runAllTimers();
1846
+ });
1847
+
1848
+ const bounds = ref.current?.getBounds(4, 4);
1849
+ expect(bounds).toEqual({
1850
+ height: 33,
1851
+ width: 41,
1852
+ x: 696,
1853
+ y: 164,
1854
+ });
1855
+ });
1856
+
1857
+ test("Ref getBounds entire grid", async () => {
1858
+ const spy = vi.fn();
1859
+ vi.useFakeTimers();
1860
+ const ref = React.createRef<DataEditorRef>();
1861
+ render(<DataEditor {...basicProps} onCellEdited={spy} ref={ref} rowMarkers="both" />, {
1862
+ wrapper: Context,
1863
+ });
1864
+ const scroller = prep(false);
1865
+
1866
+ assert(scroller !== null);
1867
+
1868
+ act(() => {
1869
+ vi.runAllTimers();
1870
+ });
1871
+
1872
+ vi.spyOn(scroller, "scrollWidth", "get").mockImplementation(() => 1000);
1873
+ vi.spyOn(scroller, "scrollHeight", "get").mockImplementation(() => 1000);
1874
+
1875
+ const bounds = ref.current?.getBounds();
1876
+ expect(bounds).toEqual({
1877
+ height: 1000,
1878
+ width: 1000,
1879
+ x: 0,
1880
+ y: 0,
1881
+ });
1882
+ });
1883
+
1884
+ test("Ctrl+Home", async () => {
1885
+ const spy = vi.fn();
1886
+ vi.useFakeTimers();
1887
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
1888
+ wrapper: Context,
1889
+ });
1890
+ prep();
1891
+
1892
+ const canvas = screen.getByTestId("data-grid-canvas");
1893
+ sendClick(canvas, {
1894
+ clientX: 300, // Col B
1895
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1896
+ });
1897
+
1898
+ spy.mockClear();
1899
+ fireEvent.keyDown(canvas, {
1900
+ key: "Home",
1901
+ ctrlKey: true,
1902
+ });
1903
+
1904
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [0, 0] }) }));
1905
+ });
1906
+
1907
+ test("Ctrl+End", async () => {
1908
+ const spy = vi.fn();
1909
+ vi.useFakeTimers();
1910
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
1911
+ wrapper: Context,
1912
+ });
1913
+ prep();
1914
+
1915
+ const canvas = screen.getByTestId("data-grid-canvas");
1916
+ sendClick(canvas, {
1917
+ clientX: 300, // Col B
1918
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1919
+ });
1920
+
1921
+ spy.mockClear();
1922
+ fireEvent.keyDown(canvas, {
1923
+ key: "End",
1924
+ ctrlKey: true,
1925
+ });
1926
+
1927
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [10, 1000] }) }));
1928
+ });
1929
+
1930
+ test("Ctrl+Shift+Home", async () => {
1931
+ const spy = vi.fn();
1932
+ vi.useFakeTimers();
1933
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
1934
+ wrapper: Context,
1935
+ });
1936
+ prep();
1937
+
1938
+ const canvas = screen.getByTestId("data-grid-canvas");
1939
+ sendClick(canvas, {
1940
+ clientX: 300, // Col B
1941
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1942
+ });
1943
+
1944
+ spy.mockClear();
1945
+ fireEvent.keyDown(canvas, {
1946
+ key: "Home",
1947
+ ctrlKey: true,
1948
+ shiftKey: true,
1949
+ });
1950
+
1951
+ expect(spy).toBeCalledWith(
1952
+ expect.objectContaining({
1953
+ columns: CompactSelection.empty(),
1954
+ current: {
1955
+ cell: [1, 1],
1956
+ range: {
1957
+ height: 2,
1958
+ width: 2,
1959
+ x: 0,
1960
+ y: 0,
1961
+ },
1962
+ rangeStack: [],
1963
+ },
1964
+ rows: CompactSelection.empty(),
1965
+ })
1966
+ );
1967
+ });
1968
+
1969
+ test("Ctrl+Shift+End", async () => {
1970
+ const spy = vi.fn();
1971
+ vi.useFakeTimers();
1972
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
1973
+ wrapper: Context,
1974
+ });
1975
+ prep();
1976
+
1977
+ const canvas = screen.getByTestId("data-grid-canvas");
1978
+ sendClick(canvas, {
1979
+ clientX: 300, // Col B
1980
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
1981
+ });
1982
+
1983
+ spy.mockClear();
1984
+ fireEvent.keyDown(canvas, {
1985
+ key: "End",
1986
+ ctrlKey: true,
1987
+ shiftKey: true,
1988
+ });
1989
+
1990
+ expect(spy).toBeCalledWith(
1991
+ expect.objectContaining({
1992
+ columns: CompactSelection.empty(),
1993
+ current: {
1994
+ cell: [1, 1],
1995
+ range: {
1996
+ height: 999,
1997
+ width: 10,
1998
+ x: 1,
1999
+ y: 1,
2000
+ },
2001
+ rangeStack: [],
2002
+ },
2003
+ rows: CompactSelection.empty(),
2004
+ })
2005
+ );
2006
+ });
2007
+
2008
+ test("Page down", async () => {
2009
+ const spy = vi.fn();
2010
+ vi.useFakeTimers();
2011
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2012
+ wrapper: Context,
2013
+ });
2014
+ prep();
2015
+
2016
+ const canvas = screen.getByTestId("data-grid-canvas");
2017
+ sendClick(canvas, {
2018
+ clientX: 300, // Col B
2019
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2020
+ });
2021
+
2022
+ spy.mockClear();
2023
+ fireEvent.keyDown(canvas, {
2024
+ key: "PageDown",
2025
+ });
2026
+
2027
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 29] }) }));
2028
+ });
2029
+
2030
+ test("Page up", async () => {
2031
+ const spy = vi.fn();
2032
+ vi.useFakeTimers();
2033
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2034
+ wrapper: Context,
2035
+ });
2036
+ prep();
2037
+
2038
+ const canvas = screen.getByTestId("data-grid-canvas");
2039
+ sendClick(canvas, {
2040
+ clientX: 300, // Col B
2041
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2042
+ });
2043
+
2044
+ spy.mockClear();
2045
+ fireEvent.keyDown(canvas, {
2046
+ key: "PageUp",
2047
+ });
2048
+
2049
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 0] }) }));
2050
+ });
2051
+
2052
+ test("Arrow left", async () => {
2053
+ const spy = vi.fn();
2054
+ vi.useFakeTimers();
2055
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2056
+ wrapper: Context,
2057
+ });
2058
+ prep();
2059
+
2060
+ const canvas = screen.getByTestId("data-grid-canvas");
2061
+ sendClick(canvas, {
2062
+ clientX: 300, // Col B
2063
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2064
+ });
2065
+
2066
+ spy.mockClear();
2067
+ fireEvent.keyDown(canvas, {
2068
+ key: "ArrowLeft",
2069
+ });
2070
+
2071
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [0, 1] }) }));
2072
+ });
2073
+
2074
+ test("Arrow shift left", async () => {
2075
+ const spy = vi.fn();
2076
+ vi.useFakeTimers();
2077
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2078
+ wrapper: Context,
2079
+ });
2080
+ prep();
2081
+
2082
+ const canvas = screen.getByTestId("data-grid-canvas");
2083
+ sendClick(canvas, {
2084
+ clientX: 300, // Col B
2085
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2086
+ });
2087
+
2088
+ spy.mockClear();
2089
+ fireEvent.keyDown(canvas, {
2090
+ shiftKey: true,
2091
+ key: "ArrowLeft",
2092
+ });
2093
+
2094
+ expect(spy).toBeCalledWith(
2095
+ expect.objectContaining({
2096
+ current: expect.objectContaining({ cell: [1, 1], range: { x: 0, y: 1, width: 2, height: 1 } }),
2097
+ })
2098
+ );
2099
+ });
2100
+
2101
+ test("Arrow right", async () => {
2102
+ const spy = vi.fn();
2103
+ vi.useFakeTimers();
2104
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2105
+ wrapper: Context,
2106
+ });
2107
+ prep();
2108
+
2109
+ const canvas = screen.getByTestId("data-grid-canvas");
2110
+ sendClick(canvas, {
2111
+ clientX: 300, // Col B
2112
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2113
+ });
2114
+
2115
+ spy.mockClear();
2116
+ fireEvent.keyDown(canvas, {
2117
+ key: "ArrowRight",
2118
+ });
2119
+
2120
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [2, 1] }) }));
2121
+ });
2122
+
2123
+ test("Arrow shift right", async () => {
2124
+ const spy = vi.fn();
2125
+ vi.useFakeTimers();
2126
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2127
+ wrapper: Context,
2128
+ });
2129
+ prep();
2130
+
2131
+ const canvas = screen.getByTestId("data-grid-canvas");
2132
+ sendClick(canvas, {
2133
+ clientX: 300, // Col B
2134
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2135
+ });
2136
+
2137
+ spy.mockClear();
2138
+ fireEvent.keyDown(canvas, {
2139
+ shiftKey: true,
2140
+ key: "ArrowRight",
2141
+ });
2142
+
2143
+ expect(spy).toBeCalledWith(
2144
+ expect.objectContaining({
2145
+ current: expect.objectContaining({ cell: [1, 1], range: { x: 1, y: 1, width: 2, height: 1 } }),
2146
+ })
2147
+ );
2148
+ });
2149
+
2150
+ test("Tab navigation", async () => {
2151
+ const spy = vi.fn();
2152
+ vi.useFakeTimers();
2153
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2154
+ wrapper: Context,
2155
+ });
2156
+ prep();
2157
+
2158
+ const canvas = screen.getByTestId("data-grid-canvas");
2159
+ sendClick(canvas, {
2160
+ clientX: 300, // Col B
2161
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2162
+ });
2163
+
2164
+ spy.mockClear();
2165
+ fireEvent.keyDown(canvas, {
2166
+ key: "Tab",
2167
+ });
2168
+
2169
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [2, 1] }) }));
2170
+
2171
+ spy.mockClear();
2172
+ fireEvent.keyDown(canvas, {
2173
+ key: "Tab",
2174
+ shiftKey: true,
2175
+ });
2176
+
2177
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 1] }) }));
2178
+ });
2179
+
2180
+ test("Arrow down", async () => {
2181
+ const spy = vi.fn();
2182
+ vi.useFakeTimers();
2183
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2184
+ wrapper: Context,
2185
+ });
2186
+ prep();
2187
+
2188
+ const canvas = screen.getByTestId("data-grid-canvas");
2189
+ sendClick(canvas, {
2190
+ clientX: 300, // Col B
2191
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
2192
+ });
2193
+
2194
+ spy.mockClear();
2195
+ fireEvent.keyDown(canvas, {
2196
+ key: "ArrowDown",
2197
+ });
2198
+
2199
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 2] }) }));
2200
+ });
2201
+
2202
+ test("Arrow up", async () => {
2203
+ const spy = vi.fn();
2204
+ vi.useFakeTimers();
2205
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
2206
+ wrapper: Context,
2207
+ });
2208
+ prep();
2209
+
2210
+ const canvas = screen.getByTestId("data-grid-canvas");
2211
+ sendClick(canvas, {
2212
+ clientX: 300, // Col B
2213
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2214
+ });
2215
+
2216
+ spy.mockClear();
2217
+ fireEvent.keyDown(canvas, {
2218
+ key: "ArrowUp",
2219
+ });
2220
+
2221
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 1] }) }));
2222
+ });
2223
+
2224
+ test("Freeze area reported", async () => {
2225
+ const spy = vi.fn();
2226
+ vi.useFakeTimers();
2227
+ render(
2228
+ <EventedDataEditor {...basicProps} freezeTrailingRows={2} freezeColumns={3} onVisibleRegionChanged={spy} />,
2229
+ {
2230
+ wrapper: Context,
2231
+ }
2232
+ );
2233
+ prep();
2234
+
2235
+ expect(spy).toBeCalledWith(
2236
+ expect.objectContaining({
2237
+ height: 32,
2238
+ width: 8,
2239
+ x: 3,
2240
+ y: 0,
2241
+ }),
2242
+ 0,
2243
+ 0,
2244
+ expect.objectContaining({
2245
+ freezeRegion: {
2246
+ height: 32,
2247
+ width: 3,
2248
+ x: 0,
2249
+ y: 0,
2250
+ },
2251
+ freezeRegions: [
2252
+ {
2253
+ height: 32,
2254
+ width: 3,
2255
+ x: 0,
2256
+ y: 0,
2257
+ },
2258
+ {
2259
+ height: 2,
2260
+ width: 8,
2261
+ x: 3,
2262
+ y: 998,
2263
+ },
2264
+ {
2265
+ height: 2,
2266
+ width: 3,
2267
+ x: 0,
2268
+ y: 998,
2269
+ },
2270
+ ],
2271
+ selected: undefined,
2272
+ })
2273
+ );
2274
+ });
2275
+
2276
+ test("Search close", async () => {
2277
+ const spy = vi.fn();
2278
+ vi.useFakeTimers();
2279
+ render(<EventedDataEditor {...basicProps} showSearch={true} onSearchClose={spy} />, {
2280
+ wrapper: Context,
2281
+ });
2282
+ prep(false);
2283
+
2284
+ const searchClose = screen.getByTestId("search-close-button");
2285
+ fireEvent.click(searchClose);
2286
+ act(() => {
2287
+ vi.runAllTimers();
2288
+ });
2289
+ expect(spy).toBeCalled();
2290
+ });
2291
+
2292
+ test("Trigger search results", async () => {
2293
+ const spy = vi.fn();
2294
+ vi.useFakeTimers();
2295
+ render(<EventedDataEditor {...basicProps} showSearch={true} onSearchClose={spy} />, {
2296
+ wrapper: Context,
2297
+ });
2298
+ prep();
2299
+
2300
+ vi.useFakeTimers();
2301
+ const searchInput = screen.getByTestId("search-input");
2302
+ fireEvent.change(searchInput, {
2303
+ target: {
2304
+ value: "1, 2",
2305
+ },
2306
+ });
2307
+ act(() => {
2308
+ vi.advanceTimersByTime(1000);
2309
+ vi.runAllTimers();
2310
+ });
2311
+
2312
+ const searchResult = screen.getByTestId("search-result-area");
2313
+
2314
+ expect(searchResult.textContent).toBe("111 results");
2315
+
2316
+ fireEvent.keyDown(searchInput, {
2317
+ key: "Enter",
2318
+ });
2319
+ fireEvent.keyDown(searchInput, {
2320
+ shiftKey: true,
2321
+ key: "Enter",
2322
+ });
2323
+ fireEvent.keyDown(searchInput, {
2324
+ key: "Escape",
2325
+ });
2326
+
2327
+ act(() => {
2328
+ vi.runAllTimers();
2329
+ });
2330
+
2331
+ expect(spy).toHaveBeenCalled();
2332
+ });
2333
+
2334
+ test("Copy/paste", async () => {
2335
+ const spy = vi.fn();
2336
+ const pasteSpy = vi.fn((_target: any, _values: any) => true);
2337
+ vi.useFakeTimers();
2338
+ render(
2339
+ <EventedDataEditor {...basicProps} onGridSelectionChange={spy} onPaste={(...args) => pasteSpy(...args)} />,
2340
+ {
2341
+ wrapper: Context,
2342
+ }
2343
+ );
2344
+ prep(false);
2345
+
2346
+ const canvas = screen.getByTestId("data-grid-canvas");
2347
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2348
+ sendClick(canvas, {
2349
+ clientX: 300, // Col B
2350
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2351
+ });
2352
+
2353
+ act(() => {
2354
+ vi.runAllTimers();
2355
+ });
2356
+
2357
+ spy.mockClear();
2358
+ fireEvent.keyDown(canvas, {
2359
+ key: "ArrowRight",
2360
+ shiftKey: true,
2361
+ });
2362
+
2363
+ act(() => {
2364
+ vi.runAllTimers();
2365
+ });
2366
+
2367
+ expect(spy).toBeCalledWith(
2368
+ expect.objectContaining({
2369
+ current: expect.objectContaining({ cell: [1, 2], range: { x: 1, y: 2, width: 2, height: 1 } }),
2370
+ })
2371
+ );
2372
+
2373
+ fireEvent.copy(window);
2374
+ act(() => {
2375
+ vi.runAllTimers();
2376
+ });
2377
+ expect(navigator.clipboard.writeText).toBeCalledWith("1, 2\t2, 2");
2378
+
2379
+ spy.mockClear();
2380
+ fireEvent.keyDown(canvas, {
2381
+ key: "ArrowDown",
2382
+ });
2383
+
2384
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 3] }) }));
2385
+
2386
+ fireEvent.paste(window);
2387
+ act(() => {
2388
+ vi.runAllTimers();
2389
+ });
2390
+ vi.useRealTimers();
2391
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2392
+ expect(pasteSpy).toBeCalledWith(
2393
+ [1, 3],
2394
+ [
2395
+ ["Sunday", "Dogs", "https://google.com"],
2396
+ ["Monday", "Cats", "https://google.com"],
2397
+ ["Tuesday", "Turtles", "https://google.com"],
2398
+ ["Wednesday", "Bears", "https://google.com"],
2399
+ ["Thursday", "L ions", "https://google.com"],
2400
+ ["Friday", "Pigs", "https://google.com"],
2401
+ [
2402
+ "Saturday",
2403
+ 'Turkeys and some "quotes" and\na new line char "more quotes" plus a tab .',
2404
+ "https://google.com",
2405
+ ],
2406
+ ]
2407
+ );
2408
+ });
2409
+
2410
+ test("Paste out of range does not crash", async () => {
2411
+ vi.useFakeTimers();
2412
+ render(<EventedDataEditor {...basicProps} onPaste={true} />, {
2413
+ wrapper: Context,
2414
+ });
2415
+ prep(false);
2416
+
2417
+ const canvas = screen.getByTestId("data-grid-canvas");
2418
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2419
+ sendClick(canvas, {
2420
+ clientX: 300, // Col B
2421
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2422
+ });
2423
+
2424
+ act(() => {
2425
+ vi.runAllTimers();
2426
+ });
2427
+
2428
+ fireEvent.keyDown(canvas, {
2429
+ key: "ArrowRight",
2430
+ ctrlKey: true,
2431
+ });
2432
+
2433
+ act(() => {
2434
+ vi.runAllTimers();
2435
+ });
2436
+
2437
+ fireEvent.paste(window);
2438
+ act(() => {
2439
+ vi.runAllTimers();
2440
+ });
2441
+ vi.useRealTimers();
2442
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2443
+ });
2444
+
2445
+ test("Cut cell", async () => {
2446
+ const spy = vi.fn();
2447
+ const editSpy = vi.fn();
2448
+ vi.useFakeTimers();
2449
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} onCellsEdited={editSpy} />, {
2450
+ wrapper: Context,
2451
+ });
2452
+ prep(false);
2453
+
2454
+ const canvas = screen.getByTestId("data-grid-canvas");
2455
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2456
+ sendClick(canvas, {
2457
+ clientX: 300, // Col B
2458
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2459
+ });
2460
+
2461
+ act(() => {
2462
+ vi.runAllTimers();
2463
+ });
2464
+
2465
+ fireEvent.keyDown(canvas, {
2466
+ key: "ArrowRight",
2467
+ shiftKey: true,
2468
+ });
2469
+
2470
+ act(() => {
2471
+ vi.runAllTimers();
2472
+ });
2473
+
2474
+ fireEvent.cut(window);
2475
+ vi.useRealTimers();
2476
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2477
+ expect(navigator.clipboard.writeText).toBeCalledWith("1, 2\t2, 2");
2478
+ expect(editSpy).toHaveBeenCalledWith([
2479
+ {
2480
+ location: [1, 2],
2481
+ value: expect.objectContaining({ data: "" }),
2482
+ },
2483
+ {
2484
+ location: [2, 2],
2485
+ value: expect.objectContaining({ data: "" }),
2486
+ },
2487
+ ]);
2488
+ });
2489
+
2490
+ test("Paste custom cell does not crash", async () => {
2491
+ vi.useFakeTimers();
2492
+
2493
+ // eslint-disable-next-line unicorn/consistent-function-scoping
2494
+ const alwaysCustomCell = (_cell: Item): GridCell => {
2495
+ return {
2496
+ kind: GridCellKind.Custom,
2497
+ allowOverlay: true,
2498
+ data: "custom-cell-data",
2499
+ copyData: "custom-cell-copy-data",
2500
+ };
2501
+ };
2502
+
2503
+ const spy = vi.fn();
2504
+
2505
+ render(
2506
+ <EventedDataEditor
2507
+ {...basicProps}
2508
+ getCellContent={alwaysCustomCell}
2509
+ customRenderers={[
2510
+ {
2511
+ kind: GridCellKind.Custom,
2512
+ draw: () => true,
2513
+ onPaste: spy,
2514
+ isMatch: (_cell: CustomCell): _cell is CustomCell => true,
2515
+ },
2516
+ ]}
2517
+ />,
2518
+ {
2519
+ wrapper: Context,
2520
+ }
2521
+ );
2522
+ prep(false);
2523
+
2524
+ const canvas = screen.getByTestId("data-grid-canvas");
2525
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2526
+ sendClick(canvas, {
2527
+ clientX: 300, // Col B
2528
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2529
+ });
2530
+
2531
+ act(() => {
2532
+ vi.runAllTimers();
2533
+ });
2534
+
2535
+ fireEvent.paste(window);
2536
+ act(() => {
2537
+ vi.runAllTimers();
2538
+ });
2539
+ vi.useRealTimers();
2540
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2541
+
2542
+ expect(spy).toBeCalledWith(expect.anything(), "custom-cell-data");
2543
+ });
2544
+
2545
+ test("CustomCell onClick", async () => {
2546
+ vi.useFakeTimers();
2547
+
2548
+ const onClickSpy = vi.fn();
2549
+
2550
+ // eslint-disable-next-line unicorn/consistent-function-scoping, sonarjs/no-identical-functions
2551
+ const alwaysCustomCell = (_cell: Item): GridCell => {
2552
+ return {
2553
+ kind: GridCellKind.Custom,
2554
+ allowOverlay: true,
2555
+ data: "custom-cell-data",
2556
+ copyData: "custom-cell-copy-data",
2557
+ };
2558
+ };
2559
+
2560
+ render(
2561
+ <EventedDataEditor
2562
+ {...basicProps}
2563
+ getCellContent={alwaysCustomCell}
2564
+ customRenderers={[
2565
+ {
2566
+ kind: GridCellKind.Custom,
2567
+ draw: () => true,
2568
+ onClick: onClickSpy,
2569
+ isMatch: (_cell: CustomCell): _cell is CustomCell => true,
2570
+ },
2571
+ ]}
2572
+ />,
2573
+ {
2574
+ wrapper: Context,
2575
+ }
2576
+ );
2577
+ prep(false);
2578
+
2579
+ const canvas = screen.getByTestId("data-grid-canvas");
2580
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2581
+
2582
+ // mouse down col b row 2
2583
+ fireEvent.pointerDown(canvas, {
2584
+ clientX: 300,
2585
+ clientY: 36 + 32 * 2 + 16,
2586
+ });
2587
+
2588
+ // mouse move col b row 3
2589
+ fireEvent.pointerMove(canvas, {
2590
+ clientX: 300,
2591
+ clientY: 36 + 32 * 2 + 16,
2592
+ buttons: 1,
2593
+ });
2594
+
2595
+ // mouse up
2596
+ fireEvent.pointerUp(canvas, {
2597
+ clientX: 300,
2598
+ clientY: 36 + 32 * 2 + 16,
2599
+ });
2600
+
2601
+ act(() => {
2602
+ vi.runAllTimers();
2603
+ });
2604
+
2605
+ expect(onClickSpy).toBeCalled();
2606
+ });
2607
+
2608
+ test("CustomCell onClick fires with same restriction as onCellClicked", async () => {
2609
+ vi.useFakeTimers();
2610
+
2611
+ const onClickSpy = vi.fn();
2612
+
2613
+ // eslint-disable-next-line unicorn/consistent-function-scoping, sonarjs/no-identical-functions
2614
+ const alwaysCustomCell = (_cell: Item): GridCell => {
2615
+ return {
2616
+ kind: GridCellKind.Custom,
2617
+ allowOverlay: true,
2618
+ data: "custom-cell-data",
2619
+ copyData: "custom-cell-copy-data",
2620
+ };
2621
+ };
2622
+
2623
+ render(
2624
+ <EventedDataEditor
2625
+ {...basicProps}
2626
+ getCellContent={alwaysCustomCell}
2627
+ customRenderers={[
2628
+ {
2629
+ kind: GridCellKind.Custom,
2630
+ draw: () => true,
2631
+ onClick: onClickSpy,
2632
+ isMatch: (_cell: CustomCell): _cell is CustomCell => true,
2633
+ },
2634
+ ]}
2635
+ />,
2636
+ {
2637
+ wrapper: Context,
2638
+ }
2639
+ );
2640
+ prep(false);
2641
+
2642
+ const canvas = screen.getByTestId("data-grid-canvas");
2643
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2644
+
2645
+ // mouse down col b row 2
2646
+ fireEvent.pointerDown(canvas, {
2647
+ clientX: 300,
2648
+ clientY: 36 + 32 * 2 + 16,
2649
+ });
2650
+
2651
+ // mouse move col b row 3
2652
+ fireEvent.pointerMove(canvas, {
2653
+ clientX: 300,
2654
+ clientY: 36 + 32 * 3 + 16,
2655
+ buttons: 1,
2656
+ });
2657
+
2658
+ // mouse up
2659
+ fireEvent.pointerUp(canvas, {
2660
+ clientX: 300,
2661
+ clientY: 36 + 32 * 3 + 16,
2662
+ });
2663
+
2664
+ act(() => {
2665
+ vi.runAllTimers();
2666
+ });
2667
+
2668
+ expect(onClickSpy).not.toBeCalled();
2669
+ });
2670
+
2671
+ test("renderers can override internal cells", async () => {
2672
+ const spy = vi.fn();
2673
+
2674
+ vi.useFakeTimers();
2675
+ render(
2676
+ <DataEditor
2677
+ {...basicProps}
2678
+ renderers={[
2679
+ ...AllCellRenderers,
2680
+ {
2681
+ ...markerCellRenderer,
2682
+ draw: spy,
2683
+ } as InternalCellRenderer<InnerGridCell>,
2684
+ ]}
2685
+ rowMarkers="both"
2686
+ />,
2687
+ {
2688
+ wrapper: Context,
2689
+ }
2690
+ );
2691
+ prep();
2692
+ expect(spy).toHaveBeenCalledTimes(31); // Math.ceil((height - headerHeight) / rowHeight)
2693
+ });
2694
+
2695
+ test("onCellsEdited blocks onCellEdited", async () => {
2696
+ const spy = vi.fn();
2697
+ vi.useFakeTimers();
2698
+ render(<EventedDataEditor {...basicProps} onCellEdited={spy} onCellsEdited={() => true} />, {
2699
+ wrapper: Context,
2700
+ });
2701
+ prep(false);
2702
+
2703
+ const canvas = screen.getByTestId("data-grid-canvas");
2704
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2705
+ sendClick(canvas, {
2706
+ clientX: 300, // Col B
2707
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2708
+ });
2709
+
2710
+ act(() => {
2711
+ vi.runAllTimers();
2712
+ });
2713
+
2714
+ fireEvent.paste(window);
2715
+ act(() => {
2716
+ vi.runAllTimers();
2717
+ });
2718
+ vi.useRealTimers();
2719
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2720
+ expect(spy).not.toBeCalled();
2721
+ });
2722
+
2723
+ test("Copy/paste with simple getCellsForSelection", async () => {
2724
+ const spy = vi.fn();
2725
+ const pasteSpy = vi.fn((_target: any, _values: any) => true);
2726
+ vi.useFakeTimers();
2727
+ render(
2728
+ <EventedDataEditor
2729
+ {...basicProps}
2730
+ getCellsForSelection={true}
2731
+ onGridSelectionChange={spy}
2732
+ onPaste={(...args) => pasteSpy(...args)}
2733
+ />,
2734
+ {
2735
+ wrapper: Context,
2736
+ }
2737
+ );
2738
+ prep(false);
2739
+
2740
+ const canvas = screen.getByTestId("data-grid-canvas");
2741
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2742
+ sendClick(canvas, {
2743
+ clientX: 300, // Col B
2744
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
2745
+ });
2746
+
2747
+ act(() => {
2748
+ vi.runAllTimers();
2749
+ });
2750
+
2751
+ spy.mockClear();
2752
+ fireEvent.keyDown(canvas, {
2753
+ key: "ArrowRight",
2754
+ shiftKey: true,
2755
+ });
2756
+
2757
+ expect(spy).toBeCalledWith(
2758
+ expect.objectContaining({
2759
+ current: expect.objectContaining({ cell: [1, 2], range: { x: 1, y: 2, width: 2, height: 1 } }),
2760
+ })
2761
+ );
2762
+
2763
+ fireEvent.copy(window);
2764
+ act(() => {
2765
+ vi.runAllTimers();
2766
+ });
2767
+ expect(navigator.clipboard.writeText).toBeCalledWith("1, 2\t2, 2");
2768
+
2769
+ spy.mockClear();
2770
+ fireEvent.keyDown(canvas, {
2771
+ key: "ArrowDown",
2772
+ });
2773
+
2774
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 3] }) }));
2775
+
2776
+ fireEvent.paste(window);
2777
+ act(() => {
2778
+ vi.runAllTimers();
2779
+ });
2780
+ vi.useRealTimers();
2781
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2782
+ expect(pasteSpy).toBeCalledWith(
2783
+ [1, 3],
2784
+ [
2785
+ ["Sunday", "Dogs", "https://google.com"],
2786
+ ["Monday", "Cats", "https://google.com"],
2787
+ ["Tuesday", "Turtles", "https://google.com"],
2788
+ ["Wednesday", "Bears", "https://google.com"],
2789
+ ["Thursday", "L ions", "https://google.com"],
2790
+ ["Friday", "Pigs", "https://google.com"],
2791
+ [
2792
+ "Saturday",
2793
+ 'Turkeys and some "quotes" and\na new line char "more quotes" plus a tab .',
2794
+ "https://google.com",
2795
+ ],
2796
+ ]
2797
+ );
2798
+ });
2799
+
2800
+ test("Copy rows", async () => {
2801
+ vi.useFakeTimers();
2802
+ render(
2803
+ <EventedDataEditor
2804
+ {...basicProps}
2805
+ rowMarkers="both"
2806
+ gridSelection={{
2807
+ current: undefined,
2808
+ rows: CompactSelection.fromSingleSelection([3, 4]),
2809
+ columns: CompactSelection.empty(),
2810
+ }}
2811
+ />,
2812
+ {
2813
+ wrapper: Context,
2814
+ }
2815
+ );
2816
+ prep();
2817
+
2818
+ const canvas = screen.getByTestId("data-grid-canvas");
2819
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2820
+
2821
+ fireEvent.copy(window);
2822
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2823
+ expect(navigator.clipboard.writeText).toBeCalledWith(
2824
+ "Data: 0, 3\t1, 3\t2, 3\t3\tFoobar\t************\tFoobar\t\tשלום 8, 3\t# Header: 9, 3\thttps://example.com/10/3"
2825
+ );
2826
+ });
2827
+
2828
+ test("Copy cols", async () => {
2829
+ vi.useFakeTimers();
2830
+ render(
2831
+ <EventedDataEditor
2832
+ {...basicProps}
2833
+ gridSelection={{
2834
+ columns: CompactSelection.fromSingleSelection([3, 6]),
2835
+ rows: CompactSelection.empty(),
2836
+ current: undefined,
2837
+ }}
2838
+ />,
2839
+ {
2840
+ wrapper: Context,
2841
+ }
2842
+ );
2843
+ prep();
2844
+
2845
+ const canvas = screen.getByTestId("data-grid-canvas");
2846
+ vi.spyOn(document, "activeElement", "get").mockImplementation(() => canvas);
2847
+
2848
+ fireEvent.copy(window);
2849
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
2850
+ expect(navigator.clipboard.writeText).toBeCalled();
2851
+ });
2852
+
2853
+ test("Hover header does not fetch invalid cell", async () => {
2854
+ const spy = vi.fn(basicProps.getCellContent);
2855
+
2856
+ vi.useFakeTimers();
2857
+ render(<DataEditor {...basicProps} rowMarkers="both" getCellContent={spy} />, {
2858
+ wrapper: Context,
2859
+ });
2860
+ prep();
2861
+
2862
+ const canvas = screen.getByTestId("data-grid-canvas");
2863
+
2864
+ spy.mockClear();
2865
+
2866
+ fireEvent.pointerMove(canvas, {
2867
+ clientX: 300, // Col B
2868
+ clientY: 16, // Header
2869
+ });
2870
+
2871
+ expect(spy).not.toHaveBeenCalled();
2872
+ });
2873
+
2874
+ test("Blit does not crash vertical scroll", async () => {
2875
+ vi.useFakeTimers();
2876
+ render(<DataEditor {...basicProps} />, {
2877
+ wrapper: Context,
2878
+ });
2879
+ const scroller = prep();
2880
+
2881
+ const canvas = screen.getByTestId("data-grid-canvas");
2882
+ fireEvent.pointerMove(canvas, {
2883
+ clientX: 300, // Col B
2884
+ clientY: 16, // Header
2885
+ });
2886
+
2887
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
2888
+
2889
+ if (scroller !== null) {
2890
+ vi.spyOn(scroller, "scrollWidth", "get").mockImplementation(() =>
2891
+ basicProps.columns.map(c => (isSizedGridColumn(c) ? c.width : 150)).reduce((pv, cv) => pv + cv, 0)
2892
+ );
2893
+ vi.spyOn(scroller, "scrollHeight", "get").mockImplementation(() => 1000 * 32 + 36);
2894
+ vi.spyOn(scroller, "scrollLeft", "get").mockImplementation(() => 0);
2895
+ vi.spyOn(scroller, "scrollTop", "get").mockImplementation(() => 55);
2896
+ fireEvent.scroll(scroller);
2897
+ }
2898
+
2899
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
2900
+
2901
+ if (scroller !== null) {
2902
+ vi.spyOn(scroller, "scrollWidth", "get").mockImplementation(() =>
2903
+ basicProps.columns.map(c => (isSizedGridColumn(c) ? c.width : 150)).reduce((pv, cv) => pv + cv, 0)
2904
+ );
2905
+ vi.spyOn(scroller, "scrollHeight", "get").mockImplementation(() => 1000 * 32 + 36);
2906
+ vi.spyOn(scroller, "scrollLeft", "get").mockImplementation(() => 0);
2907
+ vi.spyOn(scroller, "scrollTop", "get").mockImplementation(() => 0);
2908
+ fireEvent.scroll(scroller);
2909
+ }
2910
+
2911
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
2912
+
2913
+ expect(document.body.contains(canvas)).toBe(true);
2914
+ });
2915
+
2916
+ test("Blit does not crash horizontal scroll", async () => {
2917
+ vi.useFakeTimers();
2918
+ render(
2919
+ <DataEditor
2920
+ highlightRegions={[
2921
+ {
2922
+ color: "#12345623",
2923
+ range: {
2924
+ x: 2,
2925
+ y: 2,
2926
+ width: 3,
2927
+ height: 10,
2928
+ },
2929
+ },
2930
+ ]}
2931
+ {...basicProps}
2932
+ />,
2933
+ {
2934
+ wrapper: Context,
2935
+ }
2936
+ );
2937
+ const scroller = prep();
2938
+
2939
+ const canvas = screen.getByTestId("data-grid-canvas");
2940
+ fireEvent.pointerMove(canvas, {
2941
+ clientX: 300, // Col B
2942
+ clientY: 16, // Header
2943
+ });
2944
+
2945
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
2946
+
2947
+ if (scroller !== null) {
2948
+ vi.spyOn(scroller, "scrollWidth", "get").mockImplementation(() =>
2949
+ basicProps.columns.map(c => (isSizedGridColumn(c) ? c.width : 150)).reduce((pv, cv) => pv + cv, 0)
2950
+ );
2951
+ vi.spyOn(scroller, "scrollHeight", "get").mockImplementation(() => 1000 * 32 + 36);
2952
+ vi.spyOn(scroller, "scrollLeft", "get").mockImplementation(() => 55);
2953
+ vi.spyOn(scroller, "scrollTop", "get").mockImplementation(() => 0);
2954
+ fireEvent.scroll(scroller);
2955
+ }
2956
+
2957
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
2958
+
2959
+ if (scroller !== null) {
2960
+ vi.spyOn(scroller, "scrollWidth", "get").mockImplementation(() =>
2961
+ basicProps.columns.map(c => (isSizedGridColumn(c) ? c.width : 150)).reduce((pv, cv) => pv + cv, 0)
2962
+ );
2963
+ vi.spyOn(scroller, "scrollHeight", "get").mockImplementation(() => 1000 * 32 + 36);
2964
+ vi.spyOn(scroller, "scrollLeft", "get").mockImplementation(() => 0);
2965
+ vi.spyOn(scroller, "scrollTop", "get").mockImplementation(() => 0);
2966
+ fireEvent.scroll(scroller);
2967
+ }
2968
+
2969
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
2970
+
2971
+ expect(document.body.contains(canvas)).toBe(true);
2972
+ });
2973
+
2974
+ test("New row", async () => {
2975
+ const spy = vi.fn();
2976
+ vi.useFakeTimers();
2977
+ render(
2978
+ <EventedDataEditor
2979
+ {...basicProps}
2980
+ onRowAppended={spy}
2981
+ trailingRowOptions={{
2982
+ hint: "New Row",
2983
+ sticky: true,
2984
+ }}
2985
+ />,
2986
+ {
2987
+ wrapper: Context,
2988
+ }
2989
+ );
2990
+ prep();
2991
+
2992
+ vi.useFakeTimers();
2993
+ const canvas = screen.getByTestId("data-grid-canvas");
2994
+ sendClick(canvas, {
2995
+ clientX: 300, // Col B
2996
+ clientY: 990, // Trailing row
2997
+ });
2998
+
2999
+ expect(spy).toHaveBeenCalled();
3000
+
3001
+ act(() => {
3002
+ vi.runAllTimers();
3003
+ });
3004
+
3005
+ expect(Element.prototype.scrollTo).toHaveBeenCalled();
3006
+ });
3007
+
3008
+ test("appendRow ref without trailing row", async () => {
3009
+ const spy = vi.fn();
3010
+ const ref = React.createRef<DataEditorRef>();
3011
+ vi.useFakeTimers();
3012
+ render(<EventedDataEditor {...basicProps} onRowAppended={spy} ref={ref} trailingRowOptions={undefined} />, {
3013
+ wrapper: Context,
3014
+ });
3015
+ prep();
3016
+
3017
+ await act(async () => {
3018
+ await ref.current?.appendRow(1, false);
3019
+ });
3020
+
3021
+ expect(spy).toHaveBeenCalled();
3022
+ });
3023
+
3024
+ test("appendColumn ref", async () => {
3025
+ const spy = vi.fn();
3026
+ const ref = React.createRef<DataEditorRef>();
3027
+ vi.useFakeTimers();
3028
+ render(<EventedDataEditor {...basicProps} onColumnAppended={spy} ref={ref} trailingRowOptions={undefined} />, {
3029
+ wrapper: Context,
3030
+ });
3031
+ prep();
3032
+
3033
+ await act(async () => {
3034
+ await ref.current?.appendColumn(0, false);
3035
+ });
3036
+
3037
+ expect(spy).toHaveBeenCalled();
3038
+ });
3039
+
3040
+ test("Click row marker", async () => {
3041
+ const spy = vi.fn();
3042
+ vi.useFakeTimers();
3043
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} rowMarkers="both" />, {
3044
+ wrapper: Context,
3045
+ });
3046
+ prep();
3047
+
3048
+ vi.useFakeTimers();
3049
+ const canvas = screen.getByTestId("data-grid-canvas");
3050
+ sendClick(canvas, {
3051
+ clientX: 10, // Row marker
3052
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3053
+ });
3054
+
3055
+ expect(spy).toHaveBeenCalledWith({
3056
+ columns: CompactSelection.empty(),
3057
+ rows: CompactSelection.fromSingleSelection(2),
3058
+ current: undefined,
3059
+ });
3060
+ });
3061
+
3062
+ test("Shift click row marker", async () => {
3063
+ const spy = vi.fn();
3064
+ vi.useFakeTimers();
3065
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} rowMarkers="both" />, {
3066
+ wrapper: Context,
3067
+ });
3068
+ prep();
3069
+ const canvas = screen.getByTestId("data-grid-canvas");
3070
+
3071
+ sendClick(canvas, {
3072
+ clientX: 10, // Row marker
3073
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3074
+ });
3075
+
3076
+ spy.mockClear();
3077
+
3078
+ sendClick(canvas, {
3079
+ shiftKey: true,
3080
+ clientX: 10, // Row marker
3081
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3082
+ });
3083
+
3084
+ expect(spy).toHaveBeenCalledWith({
3085
+ columns: CompactSelection.empty(),
3086
+ rows: CompactSelection.fromSingleSelection([2, 6]),
3087
+ current: undefined,
3088
+ });
3089
+ });
3090
+
3091
+ test("Drag click row marker", async () => {
3092
+ const spy = vi.fn();
3093
+ vi.useFakeTimers();
3094
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} rowMarkers="both" />, {
3095
+ wrapper: Context,
3096
+ });
3097
+ prep();
3098
+ const canvas = screen.getByTestId("data-grid-canvas");
3099
+
3100
+ fireEvent.pointerDown(canvas, {
3101
+ clientX: 10, // Row marker
3102
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3103
+ });
3104
+
3105
+ spy.mockClear();
3106
+
3107
+ fireEvent.pointerMove(canvas, {
3108
+ shiftKey: true,
3109
+ clientX: 10, // Row marker
3110
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3111
+ buttons: 1,
3112
+ });
3113
+
3114
+ fireEvent.pointerUp(canvas, {
3115
+ shiftKey: true,
3116
+ clientX: 10, // Row marker
3117
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3118
+ });
3119
+
3120
+ expect(spy).toHaveBeenCalledWith({
3121
+ columns: CompactSelection.empty(),
3122
+ rows: CompactSelection.fromSingleSelection([2, 6]),
3123
+ current: undefined,
3124
+ });
3125
+ });
3126
+
3127
+ test("Shift click row marker - no multi-select", async () => {
3128
+ const spy = vi.fn();
3129
+ vi.useFakeTimers();
3130
+ render(
3131
+ <EventedDataEditor {...basicProps} rowSelect={"single"} onGridSelectionChange={spy} rowMarkers="both" />,
3132
+ {
3133
+ wrapper: Context,
3134
+ }
3135
+ );
3136
+ prep();
3137
+ const canvas = screen.getByTestId("data-grid-canvas");
3138
+
3139
+ sendClick(canvas, {
3140
+ clientX: 10, // Row marker
3141
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3142
+ });
3143
+
3144
+ spy.mockClear();
3145
+
3146
+ sendClick(canvas, {
3147
+ shiftKey: true,
3148
+ clientX: 10, // Row marker
3149
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3150
+ });
3151
+
3152
+ expect(spy).toHaveBeenCalledWith({
3153
+ columns: CompactSelection.empty(),
3154
+ rows: CompactSelection.fromSingleSelection(5),
3155
+ current: undefined,
3156
+ });
3157
+ });
3158
+
3159
+ test("Ctrl click row marker", async () => {
3160
+ const spy = vi.fn();
3161
+ vi.useFakeTimers();
3162
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} rowMarkers="both" />, {
3163
+ wrapper: Context,
3164
+ });
3165
+ prep();
3166
+ const canvas = screen.getByTestId("data-grid-canvas");
3167
+
3168
+ sendClick(canvas, {
3169
+ clientX: 10, // Row marker
3170
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3171
+ });
3172
+
3173
+ spy.mockClear();
3174
+
3175
+ sendClick(canvas, {
3176
+ ctrlKey: true,
3177
+ clientX: 10, // Row marker
3178
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3179
+ });
3180
+
3181
+ expect(spy).toHaveBeenCalledWith({
3182
+ columns: CompactSelection.empty(),
3183
+ rows: CompactSelection.fromSingleSelection(2).add(5),
3184
+ current: undefined,
3185
+ });
3186
+
3187
+ spy.mockClear();
3188
+
3189
+ sendClick(canvas, {
3190
+ ctrlKey: true,
3191
+ clientX: 10, // Row marker
3192
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3193
+ });
3194
+
3195
+ expect(spy).toHaveBeenCalledWith({
3196
+ columns: CompactSelection.empty(),
3197
+ rows: CompactSelection.fromSingleSelection(2),
3198
+ current: undefined,
3199
+ });
3200
+ });
3201
+
3202
+ test("Ctrl click row marker - no multi", async () => {
3203
+ const spy = vi.fn();
3204
+ vi.useFakeTimers();
3205
+ render(
3206
+ <EventedDataEditor {...basicProps} rowSelect={"single"} onGridSelectionChange={spy} rowMarkers="both" />,
3207
+ {
3208
+ wrapper: Context,
3209
+ }
3210
+ );
3211
+ prep();
3212
+ const canvas = screen.getByTestId("data-grid-canvas");
3213
+
3214
+ sendClick(canvas, {
3215
+ clientX: 10, // Row marker
3216
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3217
+ });
3218
+
3219
+ spy.mockClear();
3220
+
3221
+ sendClick(canvas, {
3222
+ ctrlKey: true,
3223
+ clientX: 10, // Row marker
3224
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3225
+ });
3226
+
3227
+ expect(spy).toHaveBeenCalledWith({
3228
+ columns: CompactSelection.empty(),
3229
+ rows: CompactSelection.fromSingleSelection(5),
3230
+ current: undefined,
3231
+ });
3232
+
3233
+ spy.mockClear();
3234
+
3235
+ sendClick(canvas, {
3236
+ ctrlKey: true,
3237
+ clientX: 10, // Row marker
3238
+ clientY: 36 + 32 * 5 + 16, // Row 2 (0 indexed)
3239
+ });
3240
+
3241
+ expect(spy).toHaveBeenCalledWith({
3242
+ columns: CompactSelection.empty(),
3243
+ rows: CompactSelection.empty(),
3244
+ current: undefined,
3245
+ });
3246
+ });
3247
+
3248
+ test("Shift click grid selection", async () => {
3249
+ const spy = vi.fn();
3250
+ vi.useFakeTimers();
3251
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
3252
+ wrapper: Context,
3253
+ });
3254
+ prep();
3255
+ const canvas = screen.getByTestId("data-grid-canvas");
3256
+
3257
+ sendClick(canvas, {
3258
+ clientX: 300, // Col B
3259
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3260
+ });
3261
+
3262
+ spy.mockClear();
3263
+
3264
+ sendClick(canvas, {
3265
+ shiftKey: true,
3266
+ clientX: 400, // Col C
3267
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3268
+ });
3269
+
3270
+ expect(spy).toHaveBeenCalledWith(
3271
+ expect.objectContaining({
3272
+ current: {
3273
+ cell: [1, 2],
3274
+ range: {
3275
+ x: 1,
3276
+ y: 2,
3277
+ width: 2,
3278
+ height: 5,
3279
+ },
3280
+ rangeStack: [],
3281
+ },
3282
+ })
3283
+ );
3284
+ });
3285
+
3286
+ test("Fill down", async () => {
3287
+ const spy = vi.fn();
3288
+ const multiSpy = vi.fn();
3289
+ vi.useFakeTimers();
3290
+ render(
3291
+ <EventedDataEditor
3292
+ {...basicProps}
3293
+ keybindings={{
3294
+ downFill: true,
3295
+ }}
3296
+ onCellsEdited={multiSpy}
3297
+ onCellEdited={spy}
3298
+ />,
3299
+ {
3300
+ wrapper: Context,
3301
+ }
3302
+ );
3303
+ prep();
3304
+ const canvas = screen.getByTestId("data-grid-canvas");
3305
+
3306
+ sendClick(canvas, {
3307
+ clientX: 300, // Col B
3308
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3309
+ });
3310
+
3311
+ sendClick(canvas, {
3312
+ shiftKey: true,
3313
+ clientX: 400, // Col C
3314
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3315
+ });
3316
+
3317
+ fireEvent.keyDown(canvas, {
3318
+ keyCode: 68,
3319
+ ctrlKey: true,
3320
+ });
3321
+
3322
+ expect(spy).toHaveBeenCalledTimes(8);
3323
+ expect(multiSpy).toHaveBeenCalled();
3324
+ });
3325
+
3326
+ test("Fill right", async () => {
3327
+ const spy = vi.fn();
3328
+ const multiSpy = vi.fn();
3329
+ vi.useFakeTimers();
3330
+ render(
3331
+ <EventedDataEditor
3332
+ {...basicProps}
3333
+ keybindings={{ rightFill: true }}
3334
+ onCellEdited={spy}
3335
+ onCellsEdited={multiSpy}
3336
+ />,
3337
+ {
3338
+ wrapper: Context,
3339
+ }
3340
+ );
3341
+ prep();
3342
+ const canvas = screen.getByTestId("data-grid-canvas");
3343
+
3344
+ sendClick(canvas, {
3345
+ clientX: 300, // Col B
3346
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3347
+ });
3348
+
3349
+ sendClick(canvas, {
3350
+ shiftKey: true,
3351
+ clientX: 400, // Col C
3352
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3353
+ });
3354
+
3355
+ fireEvent.keyDown(canvas, {
3356
+ keyCode: 82,
3357
+ ctrlKey: true,
3358
+ });
3359
+
3360
+ expect(spy).toHaveBeenCalledTimes(5);
3361
+ expect(multiSpy).toHaveBeenCalled();
3362
+ });
3363
+
3364
+ test("Clear selection", async () => {
3365
+ const spy = vi.fn();
3366
+ vi.useFakeTimers();
3367
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
3368
+ wrapper: Context,
3369
+ });
3370
+ prep();
3371
+ const canvas = screen.getByTestId("data-grid-canvas");
3372
+
3373
+ sendClick(canvas, {
3374
+ clientX: 300, // Col B
3375
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3376
+ });
3377
+
3378
+ spy.mockClear();
3379
+
3380
+ sendClick(canvas, {
3381
+ shiftKey: true,
3382
+ clientX: 400, // Col C
3383
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3384
+ });
3385
+
3386
+ spy.mockClear();
3387
+
3388
+ fireEvent.keyDown(canvas, {
3389
+ key: "Escape",
3390
+ });
3391
+
3392
+ expect(spy).toBeCalledWith({
3393
+ columns: CompactSelection.empty(),
3394
+ rows: CompactSelection.empty(),
3395
+ current: undefined,
3396
+ });
3397
+ });
3398
+
3399
+ test("Delete range", async () => {
3400
+ const spy = vi.fn();
3401
+ vi.useFakeTimers();
3402
+ render(<EventedDataEditor {...basicProps} onCellEdited={spy} />, {
3403
+ wrapper: Context,
3404
+ });
3405
+ prep();
3406
+ const canvas = screen.getByTestId("data-grid-canvas");
3407
+
3408
+ sendClick(canvas, {
3409
+ clientX: 300, // Col B
3410
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3411
+ });
3412
+
3413
+ spy.mockClear();
3414
+
3415
+ sendClick(canvas, {
3416
+ shiftKey: true,
3417
+ clientX: 400, // Col C
3418
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3419
+ });
3420
+
3421
+ fireEvent.keyDown(canvas, {
3422
+ key: "Delete",
3423
+ });
3424
+
3425
+ expect(spy).toBeCalledTimes(10);
3426
+ });
3427
+
3428
+ test("Click out of bounds", async () => {
3429
+ const spy = vi.fn();
3430
+ vi.useFakeTimers();
3431
+ render(
3432
+ <EventedDataEditor {...basicProps} columns={basicProps.columns.slice(0, 2)} onGridSelectionChange={spy} />,
3433
+ {
3434
+ wrapper: Context,
3435
+ }
3436
+ );
3437
+ prep();
3438
+ const canvas = screen.getByTestId("data-grid-canvas");
3439
+
3440
+ sendClick(canvas, {
3441
+ clientX: 100, // Col A
3442
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3443
+ });
3444
+
3445
+ sendClick(canvas, {
3446
+ shiftKey: true,
3447
+ clientX: 200, // Col B
3448
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3449
+ });
3450
+
3451
+ spy.mockClear();
3452
+
3453
+ sendClick(canvas, {
3454
+ shiftKey: true,
3455
+ clientX: 700, // OOB
3456
+ clientY: 36 + 32 * 6 + 16, // Row 6 (0 indexed)
3457
+ });
3458
+
3459
+ expect(spy).toBeCalledWith({
3460
+ columns: CompactSelection.empty(),
3461
+ rows: CompactSelection.empty(),
3462
+ current: undefined,
3463
+ });
3464
+ });
3465
+
3466
+ test("Delete Column", async () => {
3467
+ const spy = vi.fn();
3468
+ vi.useFakeTimers();
3469
+ render(<EventedDataEditor {...basicProps} onCellEdited={spy} />, {
3470
+ wrapper: Context,
3471
+ });
3472
+ prep();
3473
+ const canvas = screen.getByTestId("data-grid-canvas");
3474
+
3475
+ sendClick(canvas, {
3476
+ clientX: 300, // Col B
3477
+ clientY: 16, // Header
3478
+ });
3479
+
3480
+ fireEvent.keyDown(canvas, {
3481
+ key: "Delete",
3482
+ });
3483
+
3484
+ expect(spy).toBeCalledTimes(1000);
3485
+ });
3486
+
3487
+ test("DND Columns", async () => {
3488
+ const spy = vi.fn();
3489
+ vi.useFakeTimers();
3490
+ render(<EventedDataEditor {...basicProps} onColumnMoved={spy} />, {
3491
+ wrapper: Context,
3492
+ });
3493
+ prep();
3494
+ const canvas = screen.getByTestId("data-grid-canvas");
3495
+
3496
+ fireEvent.pointerDown(canvas, {
3497
+ clientX: 300, // Col B
3498
+ clientY: 16, // Header
3499
+ });
3500
+
3501
+ fireEvent.pointerMove(canvas, {
3502
+ clientX: 250,
3503
+ clientY: 16,
3504
+ buttons: 1,
3505
+ });
3506
+
3507
+ fireEvent.pointerMove(canvas, {
3508
+ clientX: 200,
3509
+ clientY: 16,
3510
+ buttons: 1,
3511
+ });
3512
+
3513
+ fireEvent.pointerMove(canvas, {
3514
+ clientX: 150,
3515
+ clientY: 16,
3516
+ buttons: 1,
3517
+ });
3518
+
3519
+ fireEvent.pointerMove(canvas, {
3520
+ clientX: 100,
3521
+ clientY: 16,
3522
+ buttons: 1,
3523
+ });
3524
+
3525
+ fireEvent.pointerUp(canvas, {
3526
+ clientX: 100, // Col A
3527
+ clientY: 16, // Header
3528
+ });
3529
+
3530
+ fireEvent.click(canvas, {
3531
+ clientX: 100, // Col A
3532
+ clientY: 16, // Header
3533
+ });
3534
+
3535
+ expect(spy).toBeCalledWith(1, 0);
3536
+ });
3537
+
3538
+ test("Drag reorder row", async () => {
3539
+ const spy = vi.fn();
3540
+ vi.useFakeTimers();
3541
+ render(<EventedDataEditor {...basicProps} rowMarkers="number" onRowMoved={spy} />, {
3542
+ wrapper: Context,
3543
+ });
3544
+ prep();
3545
+ const canvas = screen.getByTestId("data-grid-canvas");
3546
+
3547
+ fireEvent.pointerDown(canvas, {
3548
+ clientX: 10, // Col B Right Edge
3549
+ clientY: 300, // Header
3550
+ });
3551
+
3552
+ fireEvent.pointerMove(canvas, {
3553
+ clientX: 10,
3554
+ clientY: 400,
3555
+ buttons: 1,
3556
+ });
3557
+
3558
+ fireEvent.pointerUp(canvas, {
3559
+ clientX: 10,
3560
+ clientY: 400,
3561
+ });
3562
+
3563
+ fireEvent.click(canvas, {
3564
+ clientX: 10,
3565
+ clientY: 400,
3566
+ });
3567
+
3568
+ expect(spy).toBeCalledWith(8, 11);
3569
+ });
3570
+
3571
+ test("Select range with mouse", async () => {
3572
+ const spy = vi.fn();
3573
+ vi.useFakeTimers();
3574
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
3575
+ wrapper: Context,
3576
+ });
3577
+ prep();
3578
+ const canvas = screen.getByTestId("data-grid-canvas");
3579
+
3580
+ fireEvent.pointerDown(canvas, {
3581
+ clientX: 300, // Col B
3582
+ clientY: 36 + 32 * 2 + 16, // Row 2
3583
+ });
3584
+
3585
+ spy.mockClear();
3586
+ fireEvent.pointerMove(canvas, {
3587
+ clientX: 600, // Col B
3588
+ clientY: 36 + 32 * 12 + 16, // Row 2
3589
+ buttons: 1,
3590
+ });
3591
+
3592
+ expect(spy).toBeCalledWith(
3593
+ expect.objectContaining({
3594
+ current: { cell: [1, 2], range: { height: 11, width: 3, x: 1, y: 2 }, rangeStack: [] },
3595
+ })
3596
+ );
3597
+
3598
+ fireEvent.pointerUp(canvas, {
3599
+ clientX: 600, // Col B
3600
+ clientY: 36 + 32 * 12 + 16, // Row 2
3601
+ });
3602
+ });
3603
+
3604
+ test("Select range with mouse middle click fails", async () => {
3605
+ const spy = vi.fn();
3606
+ vi.useFakeTimers();
3607
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
3608
+ wrapper: Context,
3609
+ });
3610
+ prep();
3611
+ const canvas = screen.getByTestId("data-grid-canvas");
3612
+
3613
+ sendClick(canvas, {
3614
+ button: 0,
3615
+ clientX: 300, // Col B
3616
+ clientY: 36 + 32 * 2 + 16, // Row 2
3617
+ });
3618
+
3619
+ fireEvent.pointerDown(canvas, {
3620
+ button: 1,
3621
+ clientX: 300, // Col B
3622
+ clientY: 36 + 32 * 2 + 16, // Row 2
3623
+ });
3624
+
3625
+ spy.mockClear();
3626
+ fireEvent.pointerMove(canvas, {
3627
+ clientX: 600, // Col B
3628
+ clientY: 36 + 32 * 12 + 16, // Row 2
3629
+ buttons: 1,
3630
+ });
3631
+
3632
+ expect(spy).not.toBeCalled();
3633
+
3634
+ fireEvent.pointerUp(canvas, {
3635
+ clientX: 600, // Col B
3636
+ clientY: 36 + 32 * 12 + 16, // Row 2
3637
+ });
3638
+ });
3639
+
3640
+ test("Select all", async () => {
3641
+ const spy = vi.fn();
3642
+ vi.useFakeTimers();
3643
+ render(
3644
+ <EventedDataEditor
3645
+ {...basicProps}
3646
+ experimental={{ renderStrategy: "double-buffer" }}
3647
+ rowMarkers="both"
3648
+ onGridSelectionChange={spy}
3649
+ />,
3650
+ {
3651
+ wrapper: Context,
3652
+ }
3653
+ );
3654
+ prep();
3655
+ const canvas = screen.getByTestId("data-grid-canvas");
3656
+
3657
+ sendClick(canvas, {
3658
+ clientX: 10,
3659
+ clientY: 10,
3660
+ });
3661
+
3662
+ expect(spy).toBeCalledWith({
3663
+ columns: CompactSelection.empty(),
3664
+ rows: CompactSelection.fromSingleSelection([0, 1000]),
3665
+ current: undefined,
3666
+ });
3667
+
3668
+ sendClick(canvas, {
3669
+ clientX: 10,
3670
+ clientY: 10,
3671
+ });
3672
+
3673
+ expect(spy).toBeCalledWith({
3674
+ columns: CompactSelection.empty(),
3675
+ rows: CompactSelection.empty(),
3676
+ current: undefined,
3677
+ });
3678
+ });
3679
+
3680
+ test("Draggable", async () => {
3681
+ const spy = vi.fn();
3682
+ vi.useFakeTimers();
3683
+ render(
3684
+ <EventedDataEditor
3685
+ {...basicProps}
3686
+ rowMarkers="both"
3687
+ onDragStart={e => {
3688
+ spy(e);
3689
+ e.setData("text/plain", "payload");
3690
+ }}
3691
+ isDraggable={true}
3692
+ />,
3693
+ {
3694
+ wrapper: Context,
3695
+ }
3696
+ );
3697
+ const scroller = prep();
3698
+ // const canvas = screen.getByTestId("data-grid-canvas");
3699
+
3700
+ if (scroller !== null) {
3701
+ const mockDownEv = createEvent.mouseDown(scroller);
3702
+ fireEvent(scroller, mockDownEv);
3703
+ expect(mockDownEv.defaultPrevented).toBe(false);
3704
+
3705
+ const mockEv = createEvent.dragStart(scroller);
3706
+ Object.assign(mockEv, {
3707
+ clientX: 100,
3708
+ clientY: 100,
3709
+ dataTransfer: {
3710
+ setData: () => undefined,
3711
+ setDragImage: () => undefined,
3712
+ effectAllowed: null,
3713
+ },
3714
+ });
3715
+ fireEvent(scroller, mockEv);
3716
+ }
3717
+
3718
+ expect(spy).toHaveBeenCalled();
3719
+ });
3720
+
3721
+ test("Click cell does not double-emit selectedrows/columns", async () => {
3722
+ const gridSelectionSpy = vi.fn();
3723
+ vi.useFakeTimers();
3724
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={gridSelectionSpy} />, {
3725
+ wrapper: Context,
3726
+ });
3727
+ prep();
3728
+ const canvas = screen.getByTestId("data-grid-canvas");
3729
+
3730
+ sendClick(canvas, {
3731
+ clientX: 300, // Col B
3732
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3733
+ });
3734
+
3735
+ expect(gridSelectionSpy).toBeCalledWith(
3736
+ expect.objectContaining({
3737
+ current: expect.objectContaining({ cell: [1, 2], range: { height: 1, width: 1, x: 1, y: 2 } }),
3738
+ })
3739
+ );
3740
+ gridSelectionSpy.mockClear();
3741
+
3742
+ fireEvent.keyDown(canvas, {
3743
+ key: "Escape",
3744
+ });
3745
+
3746
+ expect(gridSelectionSpy).toBeCalledWith({
3747
+ rows: CompactSelection.empty(),
3748
+ columns: CompactSelection.empty(),
3749
+ });
3750
+ });
3751
+
3752
+ test("Span expansion", async () => {
3753
+ const spy = vi.fn();
3754
+ vi.useFakeTimers();
3755
+
3756
+ const getCellContent: (typeof basicProps)["getCellContent"] = c => {
3757
+ const [col, row] = c;
3758
+
3759
+ if (row === 3 && col >= 2 && col <= 3) {
3760
+ return {
3761
+ ...basicProps.getCellContent([2, 3]),
3762
+ span: [2, 3] as const,
3763
+ };
3764
+ }
3765
+
3766
+ return basicProps.getCellContent(c);
3767
+ };
3768
+
3769
+ render(
3770
+ <EventedDataEditor
3771
+ {...basicProps}
3772
+ getCellContent={getCellContent}
3773
+ getCellsForSelection={true}
3774
+ onGridSelectionChange={spy}
3775
+ />,
3776
+ {
3777
+ wrapper: Context,
3778
+ }
3779
+ );
3780
+ prep();
3781
+
3782
+ const canvas = screen.getByTestId("data-grid-canvas");
3783
+ sendClick(canvas, {
3784
+ clientX: 350, // Col C
3785
+ clientY: 36 + 32 * 2 + 16, // Row 2 (0 indexed)
3786
+ });
3787
+
3788
+ spy.mockClear();
3789
+ fireEvent.keyDown(canvas, {
3790
+ shiftKey: true,
3791
+ key: "ArrowDown",
3792
+ });
3793
+
3794
+ expect(spy).toBeCalledWith(
3795
+ expect.objectContaining({
3796
+ current: expect.objectContaining({ cell: [2, 2], range: { x: 2, y: 2, width: 2, height: 2 } }),
3797
+ })
3798
+ );
3799
+
3800
+ spy.mockClear();
3801
+ fireEvent.keyDown(canvas, {
3802
+ key: "ArrowDown",
3803
+ });
3804
+
3805
+ expect(spy).toBeCalledWith(
3806
+ expect.objectContaining({
3807
+ current: expect.objectContaining({ cell: [2, 3], range: { x: 2, y: 3, width: 2, height: 1 } }),
3808
+ })
3809
+ );
3810
+ });
3811
+
3812
+ test("Imperative Handle works", async () => {
3813
+ const spy = vi.fn();
3814
+ vi.useFakeTimers();
3815
+ const ref = React.createRef<DataEditorRef>();
3816
+ render(<EventedDataEditor ref={ref} {...basicProps} onGridSelectionChange={spy} />, {
3817
+ wrapper: Context,
3818
+ });
3819
+ prep();
3820
+
3821
+ act(() => {
3822
+ void ref.current?.emit("delete");
3823
+ void ref.current?.emit("fill-right");
3824
+ void ref.current?.emit("fill-down");
3825
+ void ref.current?.emit("copy");
3826
+ void ref.current?.emit("paste");
3827
+
3828
+ ref.current?.scrollTo(5, 10);
3829
+ ref.current?.updateCells([{ cell: [0, 0] }]);
3830
+ });
3831
+ });
3832
+
3833
+ test("Imperative scrollTo false fire", async () => {
3834
+ vi.useFakeTimers();
3835
+ const ref = React.createRef<DataEditorRef>();
3836
+ render(<EventedDataEditor ref={ref} {...basicProps} rows={10_000} />, {
3837
+ wrapper: Context,
3838
+ });
3839
+ prep(false);
3840
+
3841
+ act(() => {
3842
+ ref.current?.scrollTo(5, 10);
3843
+ });
3844
+ act(() => {
3845
+ vi.runAllTimers();
3846
+ });
3847
+ expect(Element.prototype.scrollTo).not.toBeCalled();
3848
+ });
3849
+
3850
+ test("Imperative scrollTo cell", async () => {
3851
+ vi.useFakeTimers();
3852
+ const ref = React.createRef<DataEditorRef>();
3853
+ render(<EventedDataEditor ref={ref} {...basicProps} rows={10_000} />, {
3854
+ wrapper: Context,
3855
+ });
3856
+ prep(false);
3857
+
3858
+ act(() => {
3859
+ ref.current?.scrollTo(5, 500);
3860
+ });
3861
+ act(() => {
3862
+ vi.runAllTimers();
3863
+ });
3864
+ expect(Element.prototype.scrollTo).toBeCalledWith({
3865
+ behavior: "auto",
3866
+ left: 0,
3867
+ top: 15_101,
3868
+ });
3869
+ });
3870
+
3871
+ test("Imperative scrollTo pixel", async () => {
3872
+ vi.useFakeTimers();
3873
+ const ref = React.createRef<DataEditorRef>();
3874
+ render(<EventedDataEditor ref={ref} {...basicProps} rows={10_000} />, {
3875
+ wrapper: Context,
3876
+ });
3877
+ prep(false);
3878
+
3879
+ act(() => {
3880
+ ref.current?.scrollTo(5, {
3881
+ amount: 1500,
3882
+ unit: "px",
3883
+ });
3884
+ });
3885
+ act(() => {
3886
+ vi.runAllTimers();
3887
+ });
3888
+ expect(Element.prototype.scrollTo).toBeCalledWith({
3889
+ behavior: "auto",
3890
+ left: 0,
3891
+ top: 533,
3892
+ });
3893
+ });
3894
+
3895
+ test("Imperative scrollTo pixel start", async () => {
3896
+ vi.useFakeTimers();
3897
+ const ref = React.createRef<DataEditorRef>();
3898
+ render(<EventedDataEditor ref={ref} {...basicProps} rows={10_000} />, {
3899
+ wrapper: Context,
3900
+ });
3901
+ prep(false);
3902
+
3903
+ act(() => {
3904
+ ref.current?.scrollTo(
3905
+ 5,
3906
+ {
3907
+ amount: 1500,
3908
+ unit: "px",
3909
+ },
3910
+ undefined,
3911
+ undefined,
3912
+ undefined,
3913
+ {
3914
+ vAlign: "start",
3915
+ }
3916
+ );
3917
+ });
3918
+ act(() => {
3919
+ vi.runAllTimers();
3920
+ });
3921
+ expect(Element.prototype.scrollTo).toBeCalledWith({
3922
+ behavior: "auto",
3923
+ left: 0,
3924
+ top: 1464,
3925
+ });
3926
+ });
3927
+
3928
+ test("Imperative scrollTo pixel center", async () => {
3929
+ vi.useFakeTimers();
3930
+ const ref = React.createRef<DataEditorRef>();
3931
+ render(<EventedDataEditor ref={ref} {...basicProps} rows={10_000} />, {
3932
+ wrapper: Context,
3933
+ });
3934
+ prep(false);
3935
+
3936
+ act(() => {
3937
+ ref.current?.scrollTo(
3938
+ 5,
3939
+ {
3940
+ amount: 1500,
3941
+ unit: "px",
3942
+ },
3943
+ undefined,
3944
+ undefined,
3945
+ undefined,
3946
+ {
3947
+ vAlign: "center",
3948
+ }
3949
+ );
3950
+ });
3951
+ act(() => {
3952
+ vi.runAllTimers();
3953
+ });
3954
+ expect(Element.prototype.scrollTo).toBeCalledWith({
3955
+ behavior: "auto",
3956
+ left: 0,
3957
+ top: 998.5,
3958
+ });
3959
+ });
3960
+
3961
+ test("Imperative scrollTo pixel end", async () => {
3962
+ vi.useFakeTimers();
3963
+ const ref = React.createRef<DataEditorRef>();
3964
+ render(<EventedDataEditor ref={ref} {...basicProps} rows={10_000} />, {
3965
+ wrapper: Context,
3966
+ });
3967
+ prep(false);
3968
+
3969
+ act(() => {
3970
+ ref.current?.scrollTo(
3971
+ 5,
3972
+ {
3973
+ amount: 1500,
3974
+ unit: "px",
3975
+ },
3976
+ undefined,
3977
+ undefined,
3978
+ undefined,
3979
+ {
3980
+ vAlign: "end",
3981
+ }
3982
+ );
3983
+ });
3984
+ act(() => {
3985
+ vi.runAllTimers();
3986
+ });
3987
+ expect(Element.prototype.scrollTo).toBeCalledWith({
3988
+ behavior: "auto",
3989
+ left: 0,
3990
+ top: 533,
3991
+ });
3992
+ });
3993
+
3994
+ test("Imperative damage gets right cell", async () => {
3995
+ const spy = vi.fn(basicProps.getCellContent);
3996
+ vi.useFakeTimers();
3997
+ const ref = React.createRef<DataEditorRef>();
3998
+ render(<EventedDataEditor ref={ref} {...basicProps} rowMarkers="number" getCellContent={spy} />, {
3999
+ wrapper: Context,
4000
+ });
4001
+ prep();
4002
+
4003
+ spy.mockClear();
4004
+ act(() => {
4005
+ ref.current?.updateCells([{ cell: [1, 0] }]);
4006
+ });
4007
+
4008
+ expect(spy).toBeCalledWith([1, 0]);
4009
+ });
4010
+
4011
+ test("On-scroll does not spuriously fire on select", async () => {
4012
+ const spy = vi.fn(basicProps.getCellContent);
4013
+ vi.useFakeTimers();
4014
+ const ref = React.createRef<DataEditorRef>();
4015
+ render(<EventedDataEditor ref={ref} {...basicProps} rowMarkers="number" getCellContent={spy} />, {
4016
+ wrapper: Context,
4017
+ });
4018
+ prep(false);
4019
+
4020
+ const canvas = screen.getByTestId("data-grid-canvas");
4021
+ sendClick(canvas, {
4022
+ clientX: 300, // Col B
4023
+ clientY: 965,
4024
+ });
4025
+
4026
+ act(() => {
4027
+ vi.runAllTimers();
4028
+ });
4029
+
4030
+ expect(Element.prototype.scrollTo).not.toBeCalled();
4031
+ });
4032
+
4033
+ test("Keyboard scroll with controlled selection does not double fire", async () => {
4034
+ const spy = vi.fn(basicProps.getCellContent);
4035
+ vi.useFakeTimers();
4036
+ const ref = React.createRef<DataEditorRef>();
4037
+ render(<EventedDataEditor ref={ref} {...basicProps} rowMarkers="number" getCellContent={spy} />, {
4038
+ wrapper: Context,
4039
+ });
4040
+ prep(false);
4041
+
4042
+ const canvas = screen.getByTestId("data-grid-canvas");
4043
+ sendClick(canvas, {
4044
+ clientX: 300, // Col B
4045
+ clientY: 965,
4046
+ });
4047
+
4048
+ act(() => {
4049
+ vi.runAllTimers();
4050
+ });
4051
+
4052
+ // make sure we clear the mock in case a spurios scroll was emitted (test above)
4053
+ (Element.prototype.scrollTo as Mock).mockClear();
4054
+
4055
+ fireEvent.keyDown(canvas, { key: "ArrowDown" });
4056
+ fireEvent.keyUp(canvas, { key: "ArrowDown" });
4057
+
4058
+ act(() => {
4059
+ vi.runAllTimers();
4060
+ });
4061
+
4062
+ expect(Element.prototype.scrollTo).toBeCalledTimes(1);
4063
+ });
4064
+
4065
+ test("Ctrl Arrow keys", async () => {
4066
+ const spy = vi.fn();
4067
+ vi.useFakeTimers();
4068
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
4069
+ wrapper: Context,
4070
+ });
4071
+ prep();
4072
+
4073
+ const canvas = screen.getByTestId("data-grid-canvas");
4074
+ sendClick(canvas, {
4075
+ clientX: 300, // Col B
4076
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4077
+ });
4078
+
4079
+ spy.mockClear();
4080
+ fireEvent.keyDown(canvas, {
4081
+ key: "ArrowDown",
4082
+ ctrlKey: true,
4083
+ });
4084
+
4085
+ const cols = basicProps.columns.length;
4086
+
4087
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [1, 999] }) }));
4088
+
4089
+ spy.mockClear();
4090
+ fireEvent.keyDown(canvas, {
4091
+ key: "ArrowRight",
4092
+ ctrlKey: true,
4093
+ });
4094
+
4095
+ expect(spy).toBeCalledWith(
4096
+ expect.objectContaining({ current: expect.objectContaining({ cell: [cols - 1, 999] }) })
4097
+ );
4098
+
4099
+ spy.mockClear();
4100
+ fireEvent.keyDown(canvas, {
4101
+ key: "ArrowUp",
4102
+ ctrlKey: true,
4103
+ });
4104
+
4105
+ expect(spy).toBeCalledWith(
4106
+ expect.objectContaining({ current: expect.objectContaining({ cell: [cols - 1, 0] }) })
4107
+ );
4108
+
4109
+ spy.mockClear();
4110
+ fireEvent.keyDown(canvas, {
4111
+ key: "ArrowLeft",
4112
+ ctrlKey: true,
4113
+ });
4114
+
4115
+ expect(spy).toBeCalledWith(expect.objectContaining({ current: expect.objectContaining({ cell: [0, 0] }) }));
4116
+
4117
+ spy.mockClear();
4118
+ fireEvent.keyDown(canvas, {
4119
+ key: "ArrowDown",
4120
+ ctrlKey: true,
4121
+ shiftKey: true,
4122
+ });
4123
+
4124
+ expect(spy).toBeCalledWith(
4125
+ expect.objectContaining({
4126
+ current: expect.objectContaining({ cell: [0, 0], range: { x: 0, y: 0, width: 1, height: 1000 } }),
4127
+ })
4128
+ );
4129
+
4130
+ spy.mockClear();
4131
+ fireEvent.keyDown(canvas, {
4132
+ key: "ArrowRight",
4133
+ ctrlKey: true,
4134
+ shiftKey: true,
4135
+ });
4136
+
4137
+ expect(spy).toBeCalledWith(
4138
+ expect.objectContaining({
4139
+ current: expect.objectContaining({ cell: [0, 0], range: { x: 0, y: 0, width: cols, height: 1000 } }),
4140
+ })
4141
+ );
4142
+ });
4143
+
4144
+ test("Select range with mouse going out of bounds", async () => {
4145
+ const spy = vi.fn();
4146
+ vi.useFakeTimers();
4147
+ const columns = basicProps.columns.slice(0, 2);
4148
+ render(<EventedDataEditor {...basicProps} columns={columns} onGridSelectionChange={spy} />, {
4149
+ wrapper: Context,
4150
+ });
4151
+ prep();
4152
+ const canvas = screen.getByTestId("data-grid-canvas");
4153
+
4154
+ fireEvent.pointerDown(canvas, {
4155
+ clientX: 300, // Col B
4156
+ clientY: 36 + 32 * 2 + 16, // Row 2
4157
+ });
4158
+
4159
+ spy.mockClear();
4160
+ fireEvent.pointerMove(canvas, {
4161
+ clientX: 600, // Col B
4162
+ clientY: 36 + 32 * 12 + 16, // Row 2
4163
+ buttons: 1,
4164
+ });
4165
+
4166
+ expect(spy).toBeCalledWith(
4167
+ expect.objectContaining({
4168
+ current: expect.objectContaining({ cell: [1, 2], range: { height: 11, width: 1, x: 1, y: 2 } }),
4169
+ })
4170
+ );
4171
+
4172
+ fireEvent.pointerUp(canvas, {
4173
+ clientX: 600, // Col B
4174
+ clientY: 36 + 32 * 12 + 16, // Row 2
4175
+ });
4176
+ });
4177
+
4178
+ test("Select all keybind", async () => {
4179
+ const spy = vi.fn();
4180
+ vi.useFakeTimers();
4181
+ render(<EventedDataEditor {...basicProps} keybindings={{ selectAll: true }} onGridSelectionChange={spy} />, {
4182
+ wrapper: Context,
4183
+ });
4184
+ prep();
4185
+ const canvas = screen.getByTestId("data-grid-canvas");
4186
+
4187
+ fireEvent.keyDown(canvas, {
4188
+ key: "a",
4189
+ keyCode: 65,
4190
+ ctrlKey: true,
4191
+ });
4192
+
4193
+ expect(spy).toHaveBeenCalledWith({
4194
+ columns: CompactSelection.empty(),
4195
+ rows: CompactSelection.empty(),
4196
+ current: {
4197
+ cell: [0, 0],
4198
+ range: {
4199
+ x: 0,
4200
+ y: 0,
4201
+ width: 11,
4202
+ height: 1000,
4203
+ },
4204
+ rangeStack: [],
4205
+ },
4206
+ });
4207
+ });
4208
+
4209
+ test("Select column with blending", async () => {
4210
+ const spy = vi.fn();
4211
+ vi.useFakeTimers();
4212
+ render(
4213
+ <EventedDataEditor
4214
+ {...basicProps}
4215
+ rowSelectionBlending="mixed"
4216
+ columnSelectionBlending="mixed"
4217
+ rangeSelectionBlending="mixed"
4218
+ onGridSelectionChange={spy}
4219
+ />,
4220
+ {
4221
+ wrapper: Context,
4222
+ }
4223
+ );
4224
+ prep();
4225
+
4226
+ const canvas = screen.getByTestId("data-grid-canvas");
4227
+ sendClick(canvas, {
4228
+ clientX: 300, // Col B
4229
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4230
+ });
4231
+
4232
+ spy.mockClear();
4233
+ fireEvent.keyDown(canvas, {
4234
+ key: " ",
4235
+ ctrlKey: true,
4236
+ });
4237
+
4238
+ expect(spy).toHaveBeenCalledWith({
4239
+ columns: CompactSelection.fromSingleSelection(1),
4240
+ rows: CompactSelection.empty(),
4241
+ current: {
4242
+ cell: [1, 1],
4243
+ range: {
4244
+ x: 1,
4245
+ y: 1,
4246
+ width: 1,
4247
+ height: 1,
4248
+ },
4249
+ rangeStack: [],
4250
+ },
4251
+ });
4252
+ });
4253
+
4254
+ test("Select column", async () => {
4255
+ const spy = vi.fn();
4256
+ vi.useFakeTimers();
4257
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
4258
+ wrapper: Context,
4259
+ });
4260
+ prep();
4261
+
4262
+ const canvas = screen.getByTestId("data-grid-canvas");
4263
+ sendClick(canvas, {
4264
+ clientX: 300, // Col B
4265
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4266
+ });
4267
+
4268
+ spy.mockClear();
4269
+ fireEvent.keyDown(canvas, {
4270
+ key: " ",
4271
+ ctrlKey: true,
4272
+ });
4273
+
4274
+ expect(spy).toHaveBeenCalledWith({
4275
+ columns: CompactSelection.fromSingleSelection(1),
4276
+ rows: CompactSelection.empty(),
4277
+ current: undefined,
4278
+ });
4279
+ });
4280
+
4281
+ test("Select row with blending", async () => {
4282
+ const spy = vi.fn();
4283
+ vi.useFakeTimers();
4284
+ render(
4285
+ <EventedDataEditor
4286
+ {...basicProps}
4287
+ rowSelectionBlending="mixed"
4288
+ columnSelectionBlending="mixed"
4289
+ rangeSelectionBlending="mixed"
4290
+ onGridSelectionChange={spy}
4291
+ />,
4292
+ {
4293
+ wrapper: Context,
4294
+ }
4295
+ );
4296
+ prep();
4297
+
4298
+ const canvas = screen.getByTestId("data-grid-canvas");
4299
+ sendClick(canvas, {
4300
+ clientX: 300, // Col B
4301
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4302
+ });
4303
+
4304
+ spy.mockClear();
4305
+ fireEvent.keyDown(canvas, {
4306
+ key: " ",
4307
+ shiftKey: true,
4308
+ });
4309
+
4310
+ expect(spy).toHaveBeenCalledWith({
4311
+ columns: CompactSelection.empty(),
4312
+ rows: CompactSelection.fromSingleSelection(1),
4313
+ current: {
4314
+ cell: [1, 1],
4315
+ range: {
4316
+ x: 1,
4317
+ y: 1,
4318
+ width: 1,
4319
+ height: 1,
4320
+ },
4321
+ rangeStack: [],
4322
+ },
4323
+ });
4324
+ });
4325
+
4326
+ test("Select row", async () => {
4327
+ const spy = vi.fn();
4328
+ vi.useFakeTimers();
4329
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
4330
+ wrapper: Context,
4331
+ });
4332
+ prep();
4333
+
4334
+ const canvas = screen.getByTestId("data-grid-canvas");
4335
+ sendClick(canvas, {
4336
+ clientX: 300, // Col B
4337
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4338
+ });
4339
+
4340
+ spy.mockClear();
4341
+ fireEvent.keyDown(canvas, {
4342
+ key: " ",
4343
+ shiftKey: true,
4344
+ });
4345
+
4346
+ expect(spy).toHaveBeenCalledWith({
4347
+ columns: CompactSelection.empty(),
4348
+ rows: CompactSelection.fromSingleSelection(1),
4349
+ current: undefined,
4350
+ });
4351
+ });
4352
+
4353
+ test("Select range with mouse then permissive move", async () => {
4354
+ const spy = vi.fn();
4355
+ vi.useFakeTimers();
4356
+ render(<EventedDataEditor {...basicProps} onGridSelectionChange={spy} />, {
4357
+ wrapper: Context,
4358
+ });
4359
+ prep();
4360
+ const canvas = screen.getByTestId("data-grid-canvas");
4361
+
4362
+ fireEvent.pointerDown(canvas, {
4363
+ clientX: 300, // Col B
4364
+ clientY: 36 + 32 * 2 + 16, // Row 2
4365
+ });
4366
+
4367
+ fireEvent.pointerMove(canvas, {
4368
+ clientX: 600, // Col B
4369
+ clientY: 36 + 32 * 12 + 16, // Row 2
4370
+ buttons: 1,
4371
+ });
4372
+
4373
+ fireEvent.pointerUp(canvas, {
4374
+ clientX: 600, // Col B
4375
+ clientY: 36 + 32 * 12 + 16, // Row 2
4376
+ });
4377
+
4378
+ spy.mockClear();
4379
+
4380
+ fireEvent.keyDown(canvas, {
4381
+ key: "ArrowLeft",
4382
+ altKey: true,
4383
+ });
4384
+
4385
+ expect(spy).toBeCalledWith(
4386
+ expect.objectContaining({
4387
+ current: {
4388
+ cell: [0, 2],
4389
+ range: { height: 1, width: 1, x: 0, y: 2 },
4390
+ rangeStack: [{ height: 11, width: 3, x: 1, y: 2 }],
4391
+ },
4392
+ })
4393
+ );
4394
+ });
4395
+
4396
+ test("Does not emits header menu click when move", async () => {
4397
+ const spy = vi.fn();
4398
+
4399
+ vi.useFakeTimers();
4400
+ render(
4401
+ <DataEditor
4402
+ {...basicProps}
4403
+ columns={basicProps.columns.map(c => ({ ...c, hasMenu: true }))}
4404
+ onHeaderMenuClick={spy}
4405
+ />,
4406
+ {
4407
+ wrapper: Context,
4408
+ }
4409
+ );
4410
+ prep();
4411
+
4412
+ const canvas = screen.getByTestId("data-grid-canvas");
4413
+ fireEvent.pointerMove(canvas, {
4414
+ clientX: 300, // Col B
4415
+ clientY: 16 + 200, // Not Header
4416
+ });
4417
+
4418
+ await act(() => new Promise(r => window.setTimeout(r, 10)));
4419
+
4420
+ fireEvent.pointerDown(canvas, {
4421
+ clientX: 300, // Col B
4422
+ clientY: 16 + 200, // Not Header
4423
+ });
4424
+
4425
+ fireEvent.pointerMove(canvas, {
4426
+ clientX: 300, // Col B
4427
+ clientY: 16, // Header
4428
+ buttons: 1,
4429
+ });
4430
+
4431
+ fireEvent.pointerUp(canvas, {
4432
+ clientX: 300, // Col B
4433
+ clientY: 16, // Header
4434
+ });
4435
+
4436
+ expect(spy).not.toHaveBeenCalled();
4437
+ });
4438
+
4439
+ test("Dragging header disables vertical autoscroll", async () => {
4440
+ const spy = Element.prototype.scrollBy as Mock;
4441
+ spy.mockClear();
4442
+
4443
+ vi.useFakeTimers();
4444
+ render(<DataEditor {...basicProps} />, {
4445
+ wrapper: Context,
4446
+ });
4447
+ prep();
4448
+
4449
+ const canvas = screen.getByTestId("data-grid-canvas");
4450
+ fireEvent.pointerDown(canvas, {
4451
+ clientX: 300, // Col B
4452
+ clientY: 16, // Header
4453
+ });
4454
+
4455
+ fireEvent.pointerMove(canvas, {
4456
+ clientX: 300, // Col B
4457
+ clientY: 0,
4458
+ buttons: 1,
4459
+ });
4460
+
4461
+ await act(() => new Promise(r => window.setTimeout(r, 100)));
4462
+
4463
+ fireEvent.pointerUp(canvas, {
4464
+ clientX: 300, // Col B
4465
+ clientY: 0,
4466
+ });
4467
+
4468
+ expect(spy).not.toHaveBeenCalled();
4469
+ });
4470
+
4471
+ test("Use fill handle", async () => {
4472
+ const spy = vi.fn();
4473
+ vi.useFakeTimers();
4474
+ render(<EventedDataEditor {...basicProps} onCellEdited={spy} fillHandle={true} />, {
4475
+ wrapper: Context,
4476
+ });
4477
+ prep();
4478
+ const canvas = screen.getByTestId("data-grid-canvas");
4479
+
4480
+ sendClick(canvas, {
4481
+ clientX: 290, // Col A
4482
+ clientY: 36 + 30, // Row 2
4483
+ });
4484
+
4485
+ fireEvent.pointerDown(canvas, {
4486
+ clientX: 308, // Col A
4487
+ clientY: 36 + 30, // Row 2
4488
+ });
4489
+
4490
+ fireEvent.pointerMove(canvas, {
4491
+ clientX: 308, // Col A
4492
+ clientY: 36 + 32 * 2 + 16, // Row 2
4493
+ buttons: 1,
4494
+ });
4495
+
4496
+ fireEvent.pointerUp(canvas, {
4497
+ clientX: 308, // Col A
4498
+ clientY: 36 + 32 * 2 + 16, // Row 2
4499
+ });
4500
+
4501
+ fireEvent.click(canvas, {
4502
+ clientX: 308, // Col A
4503
+ clientY: 36 + 32 * 2 + 16, // Row 2
4504
+ });
4505
+
4506
+ expect(spy).toBeCalledTimes(2);
4507
+ });
4508
+
4509
+ test("Use fill handle diagonal", async () => {
4510
+ const spy = vi.fn();
4511
+ vi.useFakeTimers();
4512
+ render(<EventedDataEditor {...basicProps} onCellEdited={spy} fillHandle={true} />, {
4513
+ wrapper: Context,
4514
+ });
4515
+ prep();
4516
+ const canvas = screen.getByTestId("data-grid-canvas");
4517
+
4518
+ sendClick(canvas, {
4519
+ clientX: 290, // Col A
4520
+ clientY: 36 + 30, // Row 2
4521
+ });
4522
+
4523
+ fireEvent.pointerDown(canvas, {
4524
+ clientX: 308, // Col A
4525
+ clientY: 36 + 30, // Row 2
4526
+ });
4527
+
4528
+ fireEvent.pointerMove(canvas, {
4529
+ clientX: 360,
4530
+ clientY: 36 + 32 * 5 + 16, // Row 5
4531
+ buttons: 1,
4532
+ });
4533
+
4534
+ fireEvent.pointerUp(canvas, {
4535
+ clientX: 360,
4536
+ clientY: 36 + 32 * 5 + 16, // Row 5
4537
+ });
4538
+
4539
+ fireEvent.click(canvas, {
4540
+ clientX: 360,
4541
+ clientY: 36 + 32 * 5 + 16, // Row 5
4542
+ });
4543
+
4544
+ expect(spy).toBeCalledTimes(5);
4545
+ });
4546
+
4547
+ test("onFillPattern", async () => {
4548
+ const spy = vi.fn();
4549
+ vi.useFakeTimers();
4550
+ render(<EventedDataEditor {...basicProps} onFillPattern={spy} fillHandle={true} />, {
4551
+ wrapper: Context,
4552
+ });
4553
+ prep();
4554
+ const canvas = screen.getByTestId("data-grid-canvas");
4555
+
4556
+ sendClick(canvas, {
4557
+ clientX: 290, // Col A
4558
+ clientY: 36 + 30, // Row 2
4559
+ });
4560
+
4561
+ fireEvent.pointerDown(canvas, {
4562
+ clientX: 308, // Col A
4563
+ clientY: 36 + 30, // Row 2
4564
+ });
4565
+
4566
+ fireEvent.pointerMove(canvas, {
4567
+ clientX: 360,
4568
+ clientY: 36 + 32 * 5 + 16, // Row 5
4569
+ buttons: 1,
4570
+ });
4571
+
4572
+ fireEvent.pointerUp(canvas, {
4573
+ clientX: 360,
4574
+ clientY: 36 + 32 * 5 + 16, // Row 5
4575
+ });
4576
+
4577
+ fireEvent.click(canvas, {
4578
+ clientX: 360,
4579
+ clientY: 36 + 32 * 5 + 16, // Row 5
4580
+ });
4581
+
4582
+ expect(spy).toBeCalledTimes(1);
4583
+ });
4584
+
4585
+ test("Use fill handle into blank", async () => {
4586
+ const spy = vi.fn();
4587
+ vi.useFakeTimers();
4588
+ render(<EventedDataEditor {...basicProps} rows={3} onCellEdited={spy} fillHandle={true} />, {
4589
+ wrapper: Context,
4590
+ });
4591
+ prep();
4592
+ const canvas = screen.getByTestId("data-grid-canvas");
4593
+
4594
+ sendClick(canvas, {
4595
+ clientX: 290,
4596
+ clientY: 36 + 30,
4597
+ });
4598
+
4599
+ fireEvent.pointerDown(canvas, {
4600
+ clientX: 308,
4601
+ clientY: 36 + 30,
4602
+ });
4603
+
4604
+ fireEvent.pointerMove(canvas, {
4605
+ clientX: 308,
4606
+ clientY: 36 + 32 * 5 + 16,
4607
+ buttons: 1,
4608
+ });
4609
+
4610
+ fireEvent.pointerUp(canvas, {
4611
+ clientX: 308,
4612
+ clientY: 36 + 32 * 5 + 16,
4613
+ });
4614
+
4615
+ fireEvent.click(canvas, {
4616
+ clientX: 308,
4617
+ clientY: 36 + 32 * 5 + 16,
4618
+ });
4619
+
4620
+ expect(spy).toBeCalledTimes(2);
4621
+ });
4622
+
4623
+ test("Use fill handle into trailing row", async () => {
4624
+ const spy = vi.fn();
4625
+ vi.useFakeTimers();
4626
+ render(
4627
+ <EventedDataEditor
4628
+ {...basicProps}
4629
+ rows={3}
4630
+ onCellEdited={spy}
4631
+ fillHandle={true}
4632
+ onRowAppended={() => undefined}
4633
+ trailingRowOptions={{
4634
+ sticky: true,
4635
+ }}
4636
+ />,
4637
+ {
4638
+ wrapper: Context,
4639
+ }
4640
+ );
4641
+ prep();
4642
+ const canvas = screen.getByTestId("data-grid-canvas");
4643
+
4644
+ sendClick(canvas, {
4645
+ clientX: 290,
4646
+ clientY: 36 + 30,
4647
+ });
4648
+
4649
+ fireEvent.pointerDown(canvas, {
4650
+ clientX: 308,
4651
+ clientY: 36 + 30,
4652
+ });
4653
+
4654
+ fireEvent.pointerMove(canvas, {
4655
+ clientX: 308,
4656
+ clientY: 800,
4657
+ buttons: 1,
4658
+ });
4659
+
4660
+ fireEvent.pointerMove(canvas, {
4661
+ clientX: 308,
4662
+ clientY: 995,
4663
+ buttons: 1,
4664
+ });
4665
+
4666
+ fireEvent.pointerUp(canvas, {
4667
+ clientX: 308,
4668
+ clientY: 995,
4669
+ });
4670
+
4671
+ fireEvent.click(canvas, {
4672
+ clientX: 308,
4673
+ clientY: 995,
4674
+ });
4675
+
4676
+ expect(spy).toBeCalledTimes(2);
4677
+ });
4678
+
4679
+ test("Close overlay with enter key", async () => {
4680
+ const spy = vi.fn();
4681
+ vi.useFakeTimers();
4682
+ render(
4683
+ <EventedDataEditor
4684
+ experimental={{
4685
+ strict: true,
4686
+ }}
4687
+ {...basicProps}
4688
+ onGridSelectionChange={spy}
4689
+ />,
4690
+ {
4691
+ wrapper: Context,
4692
+ }
4693
+ );
4694
+ prep(false);
4695
+
4696
+ const canvas = screen.getByTestId("data-grid-canvas");
4697
+ sendClick(canvas, {
4698
+ clientX: 300, // Col B
4699
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4700
+ });
4701
+
4702
+ fireEvent.keyDown(canvas, {
4703
+ key: "Enter",
4704
+ });
4705
+
4706
+ act(() => {
4707
+ vi.runAllTimers();
4708
+ });
4709
+
4710
+ spy.mockClear();
4711
+ fireEvent.keyDown(canvas, {
4712
+ key: "Enter",
4713
+ });
4714
+
4715
+ expect(spy).toHaveBeenCalledWith({
4716
+ columns: CompactSelection.empty(),
4717
+ rows: CompactSelection.empty(),
4718
+ current: {
4719
+ cell: [1, 2],
4720
+ range: {
4721
+ x: 1,
4722
+ y: 2,
4723
+ width: 1,
4724
+ height: 1,
4725
+ },
4726
+ rangeStack: [],
4727
+ },
4728
+ });
4729
+ });
4730
+
4731
+ test("Clear selection when suddenly out of range", async () => {
4732
+ const spy = vi.fn();
4733
+ vi.useFakeTimers();
4734
+ const { rerender } = render(<EventedDataEditor {...basicProps} rows={10} onGridSelectionChange={spy} />, {
4735
+ wrapper: Context,
4736
+ });
4737
+ prep(false);
4738
+
4739
+ const canvas = screen.getByTestId("data-grid-canvas");
4740
+ sendClick(canvas, {
4741
+ clientX: 300,
4742
+ clientY: 36 + 32 * 5 + 16,
4743
+ });
4744
+
4745
+ act(() => {
4746
+ vi.runAllTimers();
4747
+ });
4748
+ spy.mockClear();
4749
+
4750
+ rerender(<EventedDataEditor {...basicProps} rows={1} onGridSelectionChange={spy} />);
4751
+
4752
+ act(() => {
4753
+ vi.runAllTimers();
4754
+ });
4755
+ expect(spy).toBeCalledWith({
4756
+ columns: CompactSelection.empty(),
4757
+ rows: CompactSelection.empty(),
4758
+ current: undefined,
4759
+ });
4760
+ });
4761
+
4762
+ test("Enter key does not trigger disallowed row fetch", async () => {
4763
+ const spy = vi.fn(basicProps.getCellContent);
4764
+ vi.useFakeTimers();
4765
+ render(
4766
+ <EventedDataEditor
4767
+ {...basicProps}
4768
+ rows={2}
4769
+ getCellContent={spy}
4770
+ onRowAppended={vi.fn()}
4771
+ trailingRowOptions={{
4772
+ sticky: true,
4773
+ tint: true,
4774
+ }}
4775
+ />,
4776
+ {
4777
+ wrapper: Context,
4778
+ }
4779
+ );
4780
+ prep(false);
4781
+
4782
+ const canvas = screen.getByTestId("data-grid-canvas");
4783
+ sendClick(canvas, {
4784
+ clientX: 300, // Col B
4785
+ clientY: 36 + 32 + 16, // Row 1 (0 indexed)
4786
+ });
4787
+
4788
+ fireEvent.keyDown(canvas, {
4789
+ key: "Enter",
4790
+ });
4791
+
4792
+ spy.mockClear();
4793
+ fireEvent.keyDown(canvas, {
4794
+ key: "Enter",
4795
+ });
4796
+
4797
+ act(() => {
4798
+ vi.runAllTimers();
4799
+ });
4800
+ expect(spy.mock.calls.findIndex(x => x[0][1] > 1)).toBe(-1);
4801
+ });
4802
+
4803
+ test("Row marker click preserves current with additive row blending", async () => {
4804
+ const spy = vi.fn();
4805
+
4806
+ vi.useFakeTimers();
4807
+ render(
4808
+ <EventedDataEditor
4809
+ {...basicProps}
4810
+ gridSelection={{
4811
+ current: {
4812
+ cell: [1, 1],
4813
+ range: { x: 1, y: 1, width: 1, height: 1 },
4814
+ rangeStack: [],
4815
+ },
4816
+ rows: CompactSelection.empty(),
4817
+ columns: CompactSelection.empty(),
4818
+ }}
4819
+ rowMarkers="both"
4820
+ rowSelectionBlending="additive"
4821
+ rangeSelectionBlending="additive"
4822
+ onGridSelectionChange={spy}
4823
+ />,
4824
+ {
4825
+ wrapper: Context,
4826
+ }
4827
+ );
4828
+ prep();
4829
+
4830
+ const canvas = screen.getByTestId("data-grid-canvas");
4831
+ sendClick(canvas, {
4832
+ clientX: 10, // Row marker
4833
+ clientY: 36 + 32 * 3 + 16, // Row 3 (0 indexed)
4834
+ });
4835
+
4836
+ expect(spy).toHaveBeenCalledWith({
4837
+ columns: CompactSelection.empty(),
4838
+ rows: CompactSelection.fromSingleSelection(3),
4839
+ current: {
4840
+ cell: [1, 1],
4841
+ range: { x: 1, y: 1, width: 1, height: 1 },
4842
+ rangeStack: [],
4843
+ },
4844
+ });
4845
+ });
4846
+
4847
+ test("Header click preserves current with additive column blending", async () => {
4848
+ const spy = vi.fn();
4849
+
4850
+ vi.useFakeTimers();
4851
+ render(
4852
+ <EventedDataEditor
4853
+ {...basicProps}
4854
+ gridSelection={{
4855
+ current: {
4856
+ cell: [1, 1],
4857
+ range: { x: 1, y: 1, width: 1, height: 1 },
4858
+ rangeStack: [],
4859
+ },
4860
+ rows: CompactSelection.empty(),
4861
+ columns: CompactSelection.empty(),
4862
+ }}
4863
+ columnSelectionBlending="additive"
4864
+ rangeSelectionBlending="additive"
4865
+ onGridSelectionChange={spy}
4866
+ />,
4867
+ {
4868
+ wrapper: Context,
4869
+ }
4870
+ );
4871
+ prep();
4872
+
4873
+ const canvas = screen.getByTestId("data-grid-canvas");
4874
+ sendClick(canvas, {
4875
+ clientX: 300, // Col B header
4876
+ clientY: 16, // Header
4877
+ });
4878
+
4879
+ expect(spy).toHaveBeenCalledWith({
4880
+ columns: CompactSelection.fromSingleSelection(1),
4881
+ rows: CompactSelection.empty(),
4882
+ current: {
4883
+ cell: [1, 1],
4884
+ range: { x: 1, y: 1, width: 1, height: 1 },
4885
+ rangeStack: [],
4886
+ },
4887
+ });
4888
+ });
4889
+
4890
+ test("Row marker drag selects rows when a rect selection exists in multi mode", async () => {
4891
+ const spy = vi.fn();
4892
+ vi.useFakeTimers();
4893
+ render(
4894
+ <EventedDataEditor
4895
+ {...basicProps}
4896
+ onGridSelectionChange={spy}
4897
+ rowMarkers="both"
4898
+ rangeSelect="multi-rect"
4899
+ rowSelectionBlending="additive"
4900
+ />,
4901
+ {
4902
+ wrapper: Context,
4903
+ }
4904
+ );
4905
+ prep();
4906
+
4907
+ const canvas = screen.getByTestId("data-grid-canvas");
4908
+
4909
+ // Create a rect selection first (click cell B2 then shift-click B4)
4910
+ sendClick(canvas, {
4911
+ clientX: 300,
4912
+ clientY: 36 + 32 * 1 + 16,
4913
+ });
4914
+ sendClick(canvas, {
4915
+ shiftKey: true,
4916
+ clientX: 300,
4917
+ clientY: 36 + 32 * 3 + 16,
4918
+ });
4919
+
4920
+ spy.mockClear();
4921
+
4922
+ // Now drag on the row marker from row 2 to row 5
4923
+ fireEvent.pointerDown(canvas, {
4924
+ clientX: 10,
4925
+ clientY: 36 + 32 * 1 + 16,
4926
+ });
4927
+
4928
+ fireEvent.pointerMove(canvas, {
4929
+ clientX: 10,
4930
+ clientY: 36 + 32 * 4 + 16,
4931
+ buttons: 1,
4932
+ });
4933
+
4934
+ fireEvent.pointerUp(canvas, {
4935
+ clientX: 10,
4936
+ clientY: 36 + 32 * 4 + 16,
4937
+ });
4938
+
4939
+ expect(spy).toHaveBeenLastCalledWith({
4940
+ columns: CompactSelection.empty(),
4941
+ rows: CompactSelection.fromSingleSelection([1, 5]),
4942
+ current: undefined,
4943
+ });
4944
+ });
4945
+ });