@usels/core 0.0.1-beta.3

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 (341) hide show
  1. package/README.md +21 -0
  2. package/dist/browser/useEventListener/index.d.mts +56 -0
  3. package/dist/browser/useEventListener/index.d.ts +56 -0
  4. package/dist/browser/useEventListener/index.js +112 -0
  5. package/dist/browser/useEventListener/index.js.map +1 -0
  6. package/dist/browser/useEventListener/index.mjs +88 -0
  7. package/dist/browser/useEventListener/index.mjs.map +1 -0
  8. package/dist/browser/useMediaQuery/demo.d.mts +5 -0
  9. package/dist/browser/useMediaQuery/demo.d.ts +5 -0
  10. package/dist/browser/useMediaQuery/demo.js +82 -0
  11. package/dist/browser/useMediaQuery/demo.js.map +1 -0
  12. package/dist/browser/useMediaQuery/demo.mjs +62 -0
  13. package/dist/browser/useMediaQuery/demo.mjs.map +1 -0
  14. package/dist/browser/useMediaQuery/index.d.mts +11 -0
  15. package/dist/browser/useMediaQuery/index.d.ts +11 -0
  16. package/dist/browser/useMediaQuery/index.js +89 -0
  17. package/dist/browser/useMediaQuery/index.js.map +1 -0
  18. package/dist/browser/useMediaQuery/index.mjs +64 -0
  19. package/dist/browser/useMediaQuery/index.mjs.map +1 -0
  20. package/dist/components/Auto/index.d.mts +33 -0
  21. package/dist/components/Auto/index.d.ts +33 -0
  22. package/dist/components/Auto/index.js +66 -0
  23. package/dist/components/Auto/index.js.map +1 -0
  24. package/dist/components/Auto/index.mjs +34 -0
  25. package/dist/components/Auto/index.mjs.map +1 -0
  26. package/dist/elements/useDocumentVisibility/demo.d.mts +5 -0
  27. package/dist/elements/useDocumentVisibility/demo.d.ts +5 -0
  28. package/dist/elements/useDocumentVisibility/demo.js +130 -0
  29. package/dist/elements/useDocumentVisibility/demo.js.map +1 -0
  30. package/dist/elements/useDocumentVisibility/demo.mjs +114 -0
  31. package/dist/elements/useDocumentVisibility/demo.mjs.map +1 -0
  32. package/dist/elements/useDocumentVisibility/index.d.mts +5 -0
  33. package/dist/elements/useDocumentVisibility/index.d.ts +5 -0
  34. package/dist/elements/useDocumentVisibility/index.js +45 -0
  35. package/dist/elements/useDocumentVisibility/index.js.map +1 -0
  36. package/dist/elements/useDocumentVisibility/index.mjs +21 -0
  37. package/dist/elements/useDocumentVisibility/index.mjs.map +1 -0
  38. package/dist/elements/useElementBounding/demo.d.mts +5 -0
  39. package/dist/elements/useElementBounding/demo.d.ts +5 -0
  40. package/dist/elements/useElementBounding/demo.js +86 -0
  41. package/dist/elements/useElementBounding/demo.js.map +1 -0
  42. package/dist/elements/useElementBounding/demo.mjs +66 -0
  43. package/dist/elements/useElementBounding/demo.mjs.map +1 -0
  44. package/dist/elements/useElementBounding/index.d.mts +46 -0
  45. package/dist/elements/useElementBounding/index.d.ts +46 -0
  46. package/dist/elements/useElementBounding/index.js +122 -0
  47. package/dist/elements/useElementBounding/index.js.map +1 -0
  48. package/dist/elements/useElementBounding/index.mjs +98 -0
  49. package/dist/elements/useElementBounding/index.mjs.map +1 -0
  50. package/dist/elements/useElementSize/demo.d.mts +5 -0
  51. package/dist/elements/useElementSize/demo.d.ts +5 -0
  52. package/dist/elements/useElementSize/demo.js +82 -0
  53. package/dist/elements/useElementSize/demo.js.map +1 -0
  54. package/dist/elements/useElementSize/demo.mjs +62 -0
  55. package/dist/elements/useElementSize/demo.mjs.map +1 -0
  56. package/dist/elements/useElementSize/index.d.mts +34 -0
  57. package/dist/elements/useElementSize/index.d.ts +34 -0
  58. package/dist/elements/useElementSize/index.js +85 -0
  59. package/dist/elements/useElementSize/index.js.map +1 -0
  60. package/dist/elements/useElementSize/index.mjs +61 -0
  61. package/dist/elements/useElementSize/index.mjs.map +1 -0
  62. package/dist/elements/useElementVisibility/demo.d.mts +5 -0
  63. package/dist/elements/useElementVisibility/demo.d.ts +5 -0
  64. package/dist/elements/useElementVisibility/demo.js +110 -0
  65. package/dist/elements/useElementVisibility/demo.js.map +1 -0
  66. package/dist/elements/useElementVisibility/demo.mjs +90 -0
  67. package/dist/elements/useElementVisibility/demo.mjs.map +1 -0
  68. package/dist/elements/useElementVisibility/index.d.mts +43 -0
  69. package/dist/elements/useElementVisibility/index.d.ts +43 -0
  70. package/dist/elements/useElementVisibility/index.js +58 -0
  71. package/dist/elements/useElementVisibility/index.js.map +1 -0
  72. package/dist/elements/useElementVisibility/index.mjs +34 -0
  73. package/dist/elements/useElementVisibility/index.mjs.map +1 -0
  74. package/dist/elements/useIntersectionObserver/demo.d.mts +5 -0
  75. package/dist/elements/useIntersectionObserver/demo.d.ts +5 -0
  76. package/dist/elements/useIntersectionObserver/demo.js +173 -0
  77. package/dist/elements/useIntersectionObserver/demo.js.map +1 -0
  78. package/dist/elements/useIntersectionObserver/demo.mjs +153 -0
  79. package/dist/elements/useIntersectionObserver/demo.mjs.map +1 -0
  80. package/dist/elements/useIntersectionObserver/index.d.mts +47 -0
  81. package/dist/elements/useIntersectionObserver/index.d.ts +47 -0
  82. package/dist/elements/useIntersectionObserver/index.js +111 -0
  83. package/dist/elements/useIntersectionObserver/index.js.map +1 -0
  84. package/dist/elements/useIntersectionObserver/index.mjs +87 -0
  85. package/dist/elements/useIntersectionObserver/index.mjs.map +1 -0
  86. package/dist/elements/useMouseInElement/demo.d.mts +5 -0
  87. package/dist/elements/useMouseInElement/demo.d.ts +5 -0
  88. package/dist/elements/useMouseInElement/demo.js +103 -0
  89. package/dist/elements/useMouseInElement/demo.js.map +1 -0
  90. package/dist/elements/useMouseInElement/demo.mjs +83 -0
  91. package/dist/elements/useMouseInElement/demo.mjs.map +1 -0
  92. package/dist/elements/useMouseInElement/index.d.mts +56 -0
  93. package/dist/elements/useMouseInElement/index.d.ts +56 -0
  94. package/dist/elements/useMouseInElement/index.js +148 -0
  95. package/dist/elements/useMouseInElement/index.js.map +1 -0
  96. package/dist/elements/useMouseInElement/index.mjs +124 -0
  97. package/dist/elements/useMouseInElement/index.mjs.map +1 -0
  98. package/dist/elements/useMutationObserver/demo.d.mts +5 -0
  99. package/dist/elements/useMutationObserver/demo.d.ts +5 -0
  100. package/dist/elements/useMutationObserver/demo.js +240 -0
  101. package/dist/elements/useMutationObserver/demo.js.map +1 -0
  102. package/dist/elements/useMutationObserver/demo.mjs +220 -0
  103. package/dist/elements/useMutationObserver/demo.mjs.map +1 -0
  104. package/dist/elements/useMutationObserver/index.d.mts +15 -0
  105. package/dist/elements/useMutationObserver/index.d.ts +15 -0
  106. package/dist/elements/useMutationObserver/index.js +69 -0
  107. package/dist/elements/useMutationObserver/index.js.map +1 -0
  108. package/dist/elements/useMutationObserver/index.mjs +45 -0
  109. package/dist/elements/useMutationObserver/index.mjs.map +1 -0
  110. package/dist/elements/useParentElement/demo.d.mts +5 -0
  111. package/dist/elements/useParentElement/demo.d.ts +5 -0
  112. package/dist/elements/useParentElement/demo.js +132 -0
  113. package/dist/elements/useParentElement/demo.js.map +1 -0
  114. package/dist/elements/useParentElement/demo.mjs +112 -0
  115. package/dist/elements/useParentElement/demo.mjs.map +1 -0
  116. package/dist/elements/useParentElement/index.d.mts +7 -0
  117. package/dist/elements/useParentElement/index.d.ts +7 -0
  118. package/dist/elements/useParentElement/index.js +47 -0
  119. package/dist/elements/useParentElement/index.js.map +1 -0
  120. package/dist/elements/useParentElement/index.mjs +23 -0
  121. package/dist/elements/useParentElement/index.mjs.map +1 -0
  122. package/dist/elements/useRef$/index.js +89 -0
  123. package/dist/elements/useRef$/index.js.map +1 -0
  124. package/dist/elements/useRef$/index.mjs +62 -0
  125. package/dist/elements/useRef$/index.mjs.map +1 -0
  126. package/dist/elements/useRef_/index.d.mts +60 -0
  127. package/dist/elements/useRef_/index.d.ts +60 -0
  128. package/dist/elements/useResizeObserver/demo.d.mts +5 -0
  129. package/dist/elements/useResizeObserver/demo.d.ts +5 -0
  130. package/dist/elements/useResizeObserver/demo.js +90 -0
  131. package/dist/elements/useResizeObserver/demo.js.map +1 -0
  132. package/dist/elements/useResizeObserver/demo.mjs +70 -0
  133. package/dist/elements/useResizeObserver/demo.mjs.map +1 -0
  134. package/dist/elements/useResizeObserver/index.d.mts +36 -0
  135. package/dist/elements/useResizeObserver/index.d.ts +36 -0
  136. package/dist/elements/useResizeObserver/index.js +74 -0
  137. package/dist/elements/useResizeObserver/index.js.map +1 -0
  138. package/dist/elements/useResizeObserver/index.mjs +49 -0
  139. package/dist/elements/useResizeObserver/index.mjs.map +1 -0
  140. package/dist/elements/useWindowFocus/demo.d.mts +5 -0
  141. package/dist/elements/useWindowFocus/demo.d.ts +5 -0
  142. package/dist/elements/useWindowFocus/demo.js +100 -0
  143. package/dist/elements/useWindowFocus/demo.js.map +1 -0
  144. package/dist/elements/useWindowFocus/demo.mjs +80 -0
  145. package/dist/elements/useWindowFocus/demo.mjs.map +1 -0
  146. package/dist/elements/useWindowFocus/index.d.mts +5 -0
  147. package/dist/elements/useWindowFocus/index.d.ts +5 -0
  148. package/dist/elements/useWindowFocus/index.js +42 -0
  149. package/dist/elements/useWindowFocus/index.js.map +1 -0
  150. package/dist/elements/useWindowFocus/index.mjs +18 -0
  151. package/dist/elements/useWindowFocus/index.mjs.map +1 -0
  152. package/dist/elements/useWindowSize/demo.d.mts +5 -0
  153. package/dist/elements/useWindowSize/demo.d.ts +5 -0
  154. package/dist/elements/useWindowSize/demo.js +78 -0
  155. package/dist/elements/useWindowSize/demo.js.map +1 -0
  156. package/dist/elements/useWindowSize/demo.mjs +58 -0
  157. package/dist/elements/useWindowSize/demo.mjs.map +1 -0
  158. package/dist/elements/useWindowSize/index.d.mts +17 -0
  159. package/dist/elements/useWindowSize/index.d.ts +17 -0
  160. package/dist/elements/useWindowSize/index.js +96 -0
  161. package/dist/elements/useWindowSize/index.js.map +1 -0
  162. package/dist/elements/useWindowSize/index.mjs +76 -0
  163. package/dist/elements/useWindowSize/index.mjs.map +1 -0
  164. package/dist/function/get/index.d.mts +45 -0
  165. package/dist/function/get/index.d.ts +45 -0
  166. package/dist/function/get/index.js +39 -0
  167. package/dist/function/get/index.js.map +1 -0
  168. package/dist/function/get/index.mjs +15 -0
  169. package/dist/function/get/index.mjs.map +1 -0
  170. package/dist/function/peek/index.d.mts +46 -0
  171. package/dist/function/peek/index.d.ts +46 -0
  172. package/dist/function/peek/index.js +39 -0
  173. package/dist/function/peek/index.js.map +1 -0
  174. package/dist/function/peek/index.mjs +15 -0
  175. package/dist/function/peek/index.mjs.map +1 -0
  176. package/dist/function/useMayObservableOptions/index.d.mts +59 -0
  177. package/dist/function/useMayObservableOptions/index.d.ts +59 -0
  178. package/dist/function/useMayObservableOptions/index.js +109 -0
  179. package/dist/function/useMayObservableOptions/index.js.map +1 -0
  180. package/dist/function/useMayObservableOptions/index.mjs +88 -0
  181. package/dist/function/useMayObservableOptions/index.mjs.map +1 -0
  182. package/dist/function/useSupported/index.d.mts +6 -0
  183. package/dist/function/useSupported/index.d.ts +6 -0
  184. package/dist/function/useSupported/index.js +37 -0
  185. package/dist/function/useSupported/index.js.map +1 -0
  186. package/dist/function/useSupported/index.mjs +13 -0
  187. package/dist/function/useSupported/index.mjs.map +1 -0
  188. package/dist/function/useWhenMounted/index.d.mts +6 -0
  189. package/dist/function/useWhenMounted/index.d.ts +6 -0
  190. package/dist/function/useWhenMounted/index.js +37 -0
  191. package/dist/function/useWhenMounted/index.js.map +1 -0
  192. package/dist/function/useWhenMounted/index.mjs +13 -0
  193. package/dist/function/useWhenMounted/index.mjs.map +1 -0
  194. package/dist/index.d.mts +24 -0
  195. package/dist/index.d.ts +24 -0
  196. package/dist/index.js +63 -0
  197. package/dist/index.js.map +1 -0
  198. package/dist/index.mjs +22 -0
  199. package/dist/index.mjs.map +1 -0
  200. package/dist/sensors/useScroll/demo.d.mts +5 -0
  201. package/dist/sensors/useScroll/demo.d.ts +5 -0
  202. package/dist/sensors/useScroll/demo.js +121 -0
  203. package/dist/sensors/useScroll/demo.js.map +1 -0
  204. package/dist/sensors/useScroll/demo.mjs +101 -0
  205. package/dist/sensors/useScroll/demo.mjs.map +1 -0
  206. package/dist/sensors/useScroll/index.d.mts +42 -0
  207. package/dist/sensors/useScroll/index.d.ts +42 -0
  208. package/dist/sensors/useScroll/index.js +149 -0
  209. package/dist/sensors/useScroll/index.js.map +1 -0
  210. package/dist/sensors/useScroll/index.mjs +125 -0
  211. package/dist/sensors/useScroll/index.mjs.map +1 -0
  212. package/dist/sensors/useWindowScroll/demo.d.mts +5 -0
  213. package/dist/sensors/useWindowScroll/demo.d.ts +5 -0
  214. package/dist/sensors/useWindowScroll/demo.js +84 -0
  215. package/dist/sensors/useWindowScroll/demo.js.map +1 -0
  216. package/dist/sensors/useWindowScroll/demo.mjs +64 -0
  217. package/dist/sensors/useWindowScroll/demo.mjs.map +1 -0
  218. package/dist/sensors/useWindowScroll/index.d.mts +9 -0
  219. package/dist/sensors/useWindowScroll/index.d.ts +9 -0
  220. package/dist/sensors/useWindowScroll/index.js +36 -0
  221. package/dist/sensors/useWindowScroll/index.js.map +1 -0
  222. package/dist/sensors/useWindowScroll/index.mjs +12 -0
  223. package/dist/sensors/useWindowScroll/index.mjs.map +1 -0
  224. package/dist/shared/configurable.d.mts +21 -0
  225. package/dist/shared/configurable.d.ts +21 -0
  226. package/dist/shared/configurable.js +39 -0
  227. package/dist/shared/configurable.js.map +1 -0
  228. package/dist/shared/configurable.mjs +12 -0
  229. package/dist/shared/configurable.mjs.map +1 -0
  230. package/dist/shared/index.d.mts +4 -0
  231. package/dist/shared/index.d.ts +4 -0
  232. package/dist/shared/index.js +31 -0
  233. package/dist/shared/index.js.map +1 -0
  234. package/dist/shared/index.mjs +7 -0
  235. package/dist/shared/index.mjs.map +1 -0
  236. package/dist/shared/normalizeTargets/index.d.mts +21 -0
  237. package/dist/shared/normalizeTargets/index.d.ts +21 -0
  238. package/dist/shared/normalizeTargets/index.js +36 -0
  239. package/dist/shared/normalizeTargets/index.js.map +1 -0
  240. package/dist/shared/normalizeTargets/index.mjs +12 -0
  241. package/dist/shared/normalizeTargets/index.mjs.map +1 -0
  242. package/dist/shared/utils.d.mts +15 -0
  243. package/dist/shared/utils.d.ts +15 -0
  244. package/dist/shared/utils.js +87 -0
  245. package/dist/shared/utils.js.map +1 -0
  246. package/dist/shared/utils.mjs +52 -0
  247. package/dist/shared/utils.mjs.map +1 -0
  248. package/dist/types.d.mts +52 -0
  249. package/dist/types.d.ts +52 -0
  250. package/dist/types.js +17 -0
  251. package/dist/types.js.map +1 -0
  252. package/dist/types.mjs +1 -0
  253. package/dist/types.mjs.map +1 -0
  254. package/package.json +54 -0
  255. package/src/browser/useEventListener/index.md +109 -0
  256. package/src/browser/useEventListener/index.spec.ts +611 -0
  257. package/src/browser/useEventListener/index.ts +242 -0
  258. package/src/browser/useMediaQuery/demo.tsx +58 -0
  259. package/src/browser/useMediaQuery/index.md +40 -0
  260. package/src/browser/useMediaQuery/index.spec.ts +267 -0
  261. package/src/browser/useMediaQuery/index.ts +96 -0
  262. package/src/components/Auto/index.tsx +65 -0
  263. package/src/elements/useDocumentVisibility/demo.tsx +111 -0
  264. package/src/elements/useDocumentVisibility/index.md +51 -0
  265. package/src/elements/useDocumentVisibility/index.spec.ts +114 -0
  266. package/src/elements/useDocumentVisibility/index.ts +26 -0
  267. package/src/elements/useElementBounding/demo.tsx +63 -0
  268. package/src/elements/useElementBounding/index.md +59 -0
  269. package/src/elements/useElementBounding/index.ts +159 -0
  270. package/src/elements/useElementSize/demo.tsx +48 -0
  271. package/src/elements/useElementSize/index.md +60 -0
  272. package/src/elements/useElementSize/index.spec.ts +295 -0
  273. package/src/elements/useElementSize/index.ts +100 -0
  274. package/src/elements/useElementVisibility/deep-observable-pattern.spec.ts +453 -0
  275. package/src/elements/useElementVisibility/demo.tsx +97 -0
  276. package/src/elements/useElementVisibility/index.md +98 -0
  277. package/src/elements/useElementVisibility/index.spec.ts +227 -0
  278. package/src/elements/useElementVisibility/index.ts +78 -0
  279. package/src/elements/useIntersectionObserver/demo.tsx +180 -0
  280. package/src/elements/useIntersectionObserver/index.md +99 -0
  281. package/src/elements/useIntersectionObserver/index.spec.ts +482 -0
  282. package/src/elements/useIntersectionObserver/index.ts +149 -0
  283. package/src/elements/useMouseInElement/demo.tsx +79 -0
  284. package/src/elements/useMouseInElement/index.md +71 -0
  285. package/src/elements/useMouseInElement/index.spec.ts +398 -0
  286. package/src/elements/useMouseInElement/index.ts +209 -0
  287. package/src/elements/useMutationObserver/demo.tsx +270 -0
  288. package/src/elements/useMutationObserver/index.md +99 -0
  289. package/src/elements/useMutationObserver/index.spec.ts +421 -0
  290. package/src/elements/useMutationObserver/index.ts +66 -0
  291. package/src/elements/useParentElement/demo.tsx +120 -0
  292. package/src/elements/useParentElement/index.md +67 -0
  293. package/src/elements/useParentElement/index.spec.ts +208 -0
  294. package/src/elements/useParentElement/index.ts +35 -0
  295. package/src/elements/useRef$/index.md +62 -0
  296. package/src/elements/useRef$/index.spec.ts +205 -0
  297. package/src/elements/useRef$/index.ts +137 -0
  298. package/src/elements/useRef$/useImperativeHandle.spec.ts +339 -0
  299. package/src/elements/useResizeObserver/demo.tsx +56 -0
  300. package/src/elements/useResizeObserver/index.md +51 -0
  301. package/src/elements/useResizeObserver/index.spec.ts +312 -0
  302. package/src/elements/useResizeObserver/index.ts +106 -0
  303. package/src/elements/useWindowFocus/demo.tsx +71 -0
  304. package/src/elements/useWindowFocus/index.md +35 -0
  305. package/src/elements/useWindowFocus/index.spec.ts +103 -0
  306. package/src/elements/useWindowFocus/index.ts +21 -0
  307. package/src/elements/useWindowSize/demo.tsx +46 -0
  308. package/src/elements/useWindowSize/index.md +50 -0
  309. package/src/elements/useWindowSize/index.spec.ts +310 -0
  310. package/src/elements/useWindowSize/index.ts +107 -0
  311. package/src/function/get/index.md +25 -0
  312. package/src/function/get/index.spec.ts +87 -0
  313. package/src/function/get/index.ts +70 -0
  314. package/src/function/peek/index.spec.ts +97 -0
  315. package/src/function/peek/index.ts +69 -0
  316. package/src/function/useMayObservableOptions/index.spec.ts +521 -0
  317. package/src/function/useMayObservableOptions/index.ts +173 -0
  318. package/src/function/useSupported/index.md +38 -0
  319. package/src/function/useSupported/index.spec.ts +116 -0
  320. package/src/function/useSupported/index.ts +14 -0
  321. package/src/function/useWhenMounted/index.md +25 -0
  322. package/src/function/useWhenMounted/index.spec.ts +120 -0
  323. package/src/function/useWhenMounted/index.ts +16 -0
  324. package/src/index.ts +25 -0
  325. package/src/sensors/useScroll/demo.tsx +98 -0
  326. package/src/sensors/useScroll/index.md +112 -0
  327. package/src/sensors/useScroll/index.spec.ts +678 -0
  328. package/src/sensors/useScroll/index.ts +201 -0
  329. package/src/sensors/useWindowScroll/demo.tsx +69 -0
  330. package/src/sensors/useWindowScroll/index.md +88 -0
  331. package/src/sensors/useWindowScroll/index.spec.ts +69 -0
  332. package/src/sensors/useWindowScroll/index.ts +11 -0
  333. package/src/shared/configurable.ts +35 -0
  334. package/src/shared/index.ts +4 -0
  335. package/src/shared/normalizeTargets/index.spec.ts +76 -0
  336. package/src/shared/normalizeTargets/index.ts +27 -0
  337. package/src/shared/utils.ts +67 -0
  338. package/src/types.ts +56 -0
  339. package/tsconfig.json +9 -0
  340. package/tsup.config.ts +10 -0
  341. package/vitest.config.ts +22 -0
@@ -0,0 +1,678 @@
1
+ // @vitest-environment jsdom
2
+ import { renderHook, act } from "@testing-library/react";
3
+ import { observable, ObservableHint } from "@legendapp/state";
4
+ import type { OpaqueObject } from "@legendapp/state";
5
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
6
+ import { useScroll } from ".";
7
+
8
+ const wrapEl = (el: Element) => observable<OpaqueObject<Element> | null>(ObservableHint.opaque(el));
9
+
10
+ const flush = () => new Promise<void>((resolve) => queueMicrotask(resolve));
11
+
12
+ function makeEl(
13
+ overrides: Partial<{
14
+ scrollLeft: number;
15
+ scrollTop: number;
16
+ scrollWidth: number;
17
+ scrollHeight: number;
18
+ clientWidth: number;
19
+ clientHeight: number;
20
+ }> = {},
21
+ ): HTMLDivElement {
22
+ const el = document.createElement("div");
23
+ const {
24
+ scrollLeft = 0,
25
+ scrollTop = 0,
26
+ scrollWidth = 500,
27
+ scrollHeight = 1000,
28
+ clientWidth = 300,
29
+ clientHeight = 400,
30
+ } = overrides;
31
+ Object.defineProperties(el, {
32
+ scrollLeft: { writable: true, configurable: true, value: scrollLeft },
33
+ scrollTop: { writable: true, configurable: true, value: scrollTop },
34
+ scrollWidth: { writable: true, configurable: true, value: scrollWidth },
35
+ scrollHeight: { writable: true, configurable: true, value: scrollHeight },
36
+ clientWidth: { writable: true, configurable: true, value: clientWidth },
37
+ clientHeight: { writable: true, configurable: true, value: clientHeight },
38
+ });
39
+ return el;
40
+ }
41
+
42
+ function setWindowDimensions(opts: {
43
+ scrollX?: number;
44
+ scrollY?: number;
45
+ innerWidth?: number;
46
+ innerHeight?: number;
47
+ docScrollWidth?: number;
48
+ docScrollHeight?: number;
49
+ }) {
50
+ if (opts.scrollX !== undefined)
51
+ Object.defineProperty(window, "scrollX", {
52
+ writable: true,
53
+ configurable: true,
54
+ value: opts.scrollX,
55
+ });
56
+ if (opts.scrollY !== undefined)
57
+ Object.defineProperty(window, "scrollY", {
58
+ writable: true,
59
+ configurable: true,
60
+ value: opts.scrollY,
61
+ });
62
+ if (opts.innerWidth !== undefined)
63
+ Object.defineProperty(window, "innerWidth", {
64
+ writable: true,
65
+ configurable: true,
66
+ value: opts.innerWidth,
67
+ });
68
+ if (opts.innerHeight !== undefined)
69
+ Object.defineProperty(window, "innerHeight", {
70
+ writable: true,
71
+ configurable: true,
72
+ value: opts.innerHeight,
73
+ });
74
+ if (opts.docScrollWidth !== undefined)
75
+ Object.defineProperty(document.documentElement, "scrollWidth", {
76
+ writable: true,
77
+ configurable: true,
78
+ value: opts.docScrollWidth,
79
+ });
80
+ if (opts.docScrollHeight !== undefined)
81
+ Object.defineProperty(document.documentElement, "scrollHeight", {
82
+ writable: true,
83
+ configurable: true,
84
+ value: opts.docScrollHeight,
85
+ });
86
+ }
87
+
88
+ describe("useScroll()", () => {
89
+ beforeEach(() => {
90
+ vi.useFakeTimers();
91
+ setWindowDimensions({
92
+ scrollX: 0,
93
+ scrollY: 0,
94
+ innerWidth: 1024,
95
+ innerHeight: 768,
96
+ docScrollWidth: 1024,
97
+ docScrollHeight: 2000,
98
+ });
99
+ });
100
+
101
+ afterEach(() => {
102
+ vi.useRealTimers();
103
+ vi.restoreAllMocks();
104
+ });
105
+
106
+ // -------------------------------------------------------------------------
107
+ // Initial values
108
+ // -------------------------------------------------------------------------
109
+
110
+ describe("initial values", () => {
111
+ it("sets element.scrollLeft/scrollTop as initial x/y values on mount", () => {
112
+ const el = makeEl({ scrollLeft: 50, scrollTop: 100 });
113
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
114
+ expect(result.current.x$.get()).toBe(50);
115
+ expect(result.current.y$.get()).toBe(100);
116
+ });
117
+
118
+ it("sets window.scrollX/scrollY as initial x/y values on mount (Window target)", () => {
119
+ setWindowDimensions({ scrollX: 20, scrollY: 40 });
120
+ const { result } = renderHook(() => useScroll(window));
121
+ expect(result.current.x$.get()).toBe(20);
122
+ expect(result.current.y$.get()).toBe(40);
123
+ });
124
+
125
+ it("sets documentElement.scrollLeft/scrollTop as initial x/y values on mount (Document target)", () => {
126
+ Object.defineProperty(document.documentElement, "scrollLeft", {
127
+ writable: true,
128
+ configurable: true,
129
+ value: 10,
130
+ });
131
+ Object.defineProperty(document.documentElement, "scrollTop", {
132
+ writable: true,
133
+ configurable: true,
134
+ value: 30,
135
+ });
136
+ const { result } = renderHook(() => useScroll(document));
137
+ expect(result.current.x$.get()).toBe(10);
138
+ expect(result.current.y$.get()).toBe(30);
139
+ });
140
+
141
+ it("initial arrivedState is top=true, left=true, right=false, bottom=false", () => {
142
+ const el = makeEl();
143
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
144
+ expect(result.current.arrivedState$.top.get()).toBe(true);
145
+ expect(result.current.arrivedState$.left.get()).toBe(true);
146
+ expect(result.current.arrivedState$.right.get()).toBe(false);
147
+ expect(result.current.arrivedState$.bottom.get()).toBe(false);
148
+ });
149
+
150
+ it("initial isScrolling is false", () => {
151
+ const el = makeEl();
152
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
153
+ // advance timers to let idle expire
154
+ act(() => { vi.runAllTimers(); });
155
+ expect(result.current.isScrolling$.get()).toBe(false);
156
+ });
157
+
158
+ it("initial directions are all false", () => {
159
+ const el = makeEl();
160
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
161
+ const d = result.current.directions$;
162
+ expect(d.left.get()).toBe(false);
163
+ expect(d.right.get()).toBe(false);
164
+ expect(d.top.get()).toBe(false);
165
+ expect(d.bottom.get()).toBe(false);
166
+ });
167
+ });
168
+
169
+ // -------------------------------------------------------------------------
170
+ // x / y updates
171
+ // -------------------------------------------------------------------------
172
+
173
+ describe("x / y updates", () => {
174
+ it("y increases when scrolling down", () => {
175
+ const el = makeEl({ scrollTop: 0 });
176
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
177
+
178
+ act(() => {
179
+ (el as any).scrollTop = 200;
180
+ el.dispatchEvent(new Event("scroll"));
181
+ });
182
+
183
+ expect(result.current.y$.get()).toBe(200);
184
+ });
185
+
186
+ it("y decreases when scrolling up", () => {
187
+ const el = makeEl({ scrollTop: 300 });
188
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
189
+
190
+ act(() => {
191
+ (el as any).scrollTop = 100;
192
+ el.dispatchEvent(new Event("scroll"));
193
+ });
194
+
195
+ expect(result.current.y$.get()).toBe(100);
196
+ });
197
+
198
+ it("x increases when scrolling right", () => {
199
+ const el = makeEl({ scrollLeft: 0 });
200
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
201
+
202
+ act(() => {
203
+ (el as any).scrollLeft = 150;
204
+ el.dispatchEvent(new Event("scroll"));
205
+ });
206
+
207
+ expect(result.current.x$.get()).toBe(150);
208
+ });
209
+
210
+ it("x decreases when scrolling left", () => {
211
+ const el = makeEl({ scrollLeft: 200 });
212
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
213
+
214
+ act(() => {
215
+ (el as any).scrollLeft = 50;
216
+ el.dispatchEvent(new Event("scroll"));
217
+ });
218
+
219
+ expect(result.current.x$.get()).toBe(50);
220
+ });
221
+
222
+ it("x does not change when only vertical scroll occurs", () => {
223
+ const el = makeEl({ scrollLeft: 0, scrollTop: 0 });
224
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
225
+
226
+ act(() => {
227
+ (el as any).scrollTop = 100;
228
+ el.dispatchEvent(new Event("scroll"));
229
+ });
230
+
231
+ expect(result.current.x$.get()).toBe(0);
232
+ });
233
+
234
+ it("y does not change when only horizontal scroll occurs", () => {
235
+ const el = makeEl({ scrollLeft: 0, scrollTop: 0 });
236
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
237
+
238
+ act(() => {
239
+ (el as any).scrollLeft = 100;
240
+ el.dispatchEvent(new Event("scroll"));
241
+ });
242
+
243
+ expect(result.current.y$.get()).toBe(0);
244
+ });
245
+ });
246
+
247
+ // -------------------------------------------------------------------------
248
+ // arrivedState
249
+ // -------------------------------------------------------------------------
250
+
251
+ describe("arrivedState", () => {
252
+ it("top=true when scrollTop is 0", () => {
253
+ const el = makeEl({ scrollTop: 0 });
254
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
255
+ expect(result.current.arrivedState$.top.get()).toBe(true);
256
+ });
257
+
258
+ it("top=false when scrollTop is greater than 0", () => {
259
+ const el = makeEl({ scrollTop: 0 });
260
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
261
+
262
+ act(() => {
263
+ (el as any).scrollTop = 10;
264
+ el.dispatchEvent(new Event("scroll"));
265
+ });
266
+
267
+ expect(result.current.arrivedState$.top.get()).toBe(false);
268
+ });
269
+
270
+ it("bottom=true when scrollTop reaches maxScrollY", () => {
271
+ // scrollHeight=1000, clientHeight=400 → maxY=600
272
+ const el = makeEl({ scrollTop: 0, scrollHeight: 1000, clientHeight: 400 });
273
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
274
+
275
+ act(() => {
276
+ (el as any).scrollTop = 600;
277
+ el.dispatchEvent(new Event("scroll"));
278
+ });
279
+
280
+ expect(result.current.arrivedState$.bottom.get()).toBe(true);
281
+ });
282
+
283
+ it("left=true when scrollLeft is 0", () => {
284
+ const el = makeEl({ scrollLeft: 0 });
285
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
286
+ expect(result.current.arrivedState$.left.get()).toBe(true);
287
+ });
288
+
289
+ it("right=true when scrollLeft reaches maxScrollX", () => {
290
+ // scrollWidth=500, clientWidth=300 → maxX=200
291
+ const el = makeEl({ scrollLeft: 0, scrollWidth: 500, clientWidth: 300 });
292
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
293
+
294
+ act(() => {
295
+ (el as any).scrollLeft = 200;
296
+ el.dispatchEvent(new Event("scroll"));
297
+ });
298
+
299
+ expect(result.current.arrivedState$.right.get()).toBe(true);
300
+ });
301
+
302
+ it("top=true when scrollTop <= offset.top with offset.top set", () => {
303
+ const el = makeEl({ scrollTop: 0 });
304
+ const { result } = renderHook(() =>
305
+ useScroll(wrapEl(el), { offset: { top: 50 } }),
306
+ );
307
+
308
+ act(() => {
309
+ (el as any).scrollTop = 30;
310
+ el.dispatchEvent(new Event("scroll"));
311
+ });
312
+
313
+ expect(result.current.arrivedState$.top.get()).toBe(true);
314
+ });
315
+
316
+ it("bottom=true when scrollTop >= maxScrollY - offset.bottom with offset.bottom set", () => {
317
+ // scrollHeight=1000, clientHeight=400 → maxY=600
318
+ const el = makeEl({ scrollTop: 0, scrollHeight: 1000, clientHeight: 400 });
319
+ const { result } = renderHook(() =>
320
+ useScroll(wrapEl(el), { offset: { bottom: 50 } }),
321
+ );
322
+
323
+ act(() => {
324
+ // 600 - 50 = 550 → bottom=true when scrollTop >= 550
325
+ (el as any).scrollTop = 550;
326
+ el.dispatchEvent(new Event("scroll"));
327
+ });
328
+
329
+ expect(result.current.arrivedState$.bottom.get()).toBe(true);
330
+ });
331
+
332
+ it("top=true and bottom=true for non-scrollable element (scrollHeight === clientHeight)", () => {
333
+ // maxY = 400 - 400 = 0
334
+ const el = makeEl({ scrollTop: 0, scrollHeight: 400, clientHeight: 400 });
335
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
336
+
337
+ act(() => {
338
+ el.dispatchEvent(new Event("scroll"));
339
+ });
340
+
341
+ expect(result.current.arrivedState$.top.get()).toBe(true);
342
+ expect(result.current.arrivedState$.bottom.get()).toBe(true);
343
+ });
344
+ });
345
+
346
+ // -------------------------------------------------------------------------
347
+ // directions
348
+ // -------------------------------------------------------------------------
349
+
350
+ describe("directions", () => {
351
+ it("bottom=true and top=false when scrolling down", () => {
352
+ const el = makeEl({ scrollTop: 100 });
353
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
354
+
355
+ act(() => {
356
+ (el as any).scrollTop = 200;
357
+ el.dispatchEvent(new Event("scroll"));
358
+ });
359
+
360
+ expect(result.current.directions$.bottom.get()).toBe(true);
361
+ expect(result.current.directions$.top.get()).toBe(false);
362
+ });
363
+
364
+ it("top=true and bottom=false when scrolling up", () => {
365
+ const el = makeEl({ scrollTop: 200 });
366
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
367
+
368
+ act(() => {
369
+ (el as any).scrollTop = 100;
370
+ el.dispatchEvent(new Event("scroll"));
371
+ });
372
+
373
+ expect(result.current.directions$.top.get()).toBe(true);
374
+ expect(result.current.directions$.bottom.get()).toBe(false);
375
+ });
376
+
377
+ it("right=true and left=false when scrolling right", () => {
378
+ const el = makeEl({ scrollLeft: 0 });
379
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
380
+
381
+ act(() => {
382
+ (el as any).scrollLeft = 100;
383
+ el.dispatchEvent(new Event("scroll"));
384
+ });
385
+
386
+ expect(result.current.directions$.right.get()).toBe(true);
387
+ expect(result.current.directions$.left.get()).toBe(false);
388
+ });
389
+
390
+ it("left=true and right=false when scrolling left", () => {
391
+ const el = makeEl({ scrollLeft: 200 });
392
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
393
+
394
+ act(() => {
395
+ (el as any).scrollLeft = 50;
396
+ el.dispatchEvent(new Event("scroll"));
397
+ });
398
+
399
+ expect(result.current.directions$.left.get()).toBe(true);
400
+ expect(result.current.directions$.right.get()).toBe(false);
401
+ });
402
+ });
403
+
404
+ // -------------------------------------------------------------------------
405
+ // isScrolling
406
+ // -------------------------------------------------------------------------
407
+
408
+ describe("isScrolling", () => {
409
+ it("becomes true when scroll event fires", () => {
410
+ const el = makeEl();
411
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
412
+
413
+ act(() => {
414
+ el.dispatchEvent(new Event("scroll"));
415
+ });
416
+
417
+ expect(result.current.isScrolling$.get()).toBe(true);
418
+ });
419
+
420
+ it("becomes false after idle time (default 200ms)", () => {
421
+ const el = makeEl();
422
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
423
+
424
+ act(() => {
425
+ el.dispatchEvent(new Event("scroll"));
426
+ vi.advanceTimersByTime(200);
427
+ });
428
+
429
+ expect(result.current.isScrolling$.get()).toBe(false);
430
+ });
431
+
432
+ it("idle option adjusts the wait time", () => {
433
+ const el = makeEl();
434
+ const { result } = renderHook(() => useScroll(wrapEl(el), { idle: 500 }));
435
+
436
+ act(() => {
437
+ el.dispatchEvent(new Event("scroll"));
438
+ vi.advanceTimersByTime(200);
439
+ });
440
+ expect(result.current.isScrolling$.get()).toBe(true);
441
+
442
+ act(() => {
443
+ vi.advanceTimersByTime(300);
444
+ });
445
+ expect(result.current.isScrolling$.get()).toBe(false);
446
+ });
447
+
448
+ it("onStop callback is called when idle expires", () => {
449
+ const onStop = vi.fn();
450
+ const el = makeEl();
451
+ renderHook(() => useScroll(wrapEl(el), { onStop, idle: 100 }));
452
+
453
+ act(() => {
454
+ el.dispatchEvent(new Event("scroll"));
455
+ vi.advanceTimersByTime(100);
456
+ });
457
+
458
+ expect(onStop).toHaveBeenCalledOnce();
459
+ });
460
+
461
+ it("timer resets when additional scroll occurs before idle expires", () => {
462
+ const onStop = vi.fn();
463
+ const el = makeEl();
464
+ renderHook(() => useScroll(wrapEl(el), { onStop, idle: 200 }));
465
+
466
+ act(() => {
467
+ el.dispatchEvent(new Event("scroll"));
468
+ vi.advanceTimersByTime(100);
469
+ el.dispatchEvent(new Event("scroll"));
470
+ vi.advanceTimersByTime(100);
471
+ });
472
+ expect(onStop).not.toHaveBeenCalled();
473
+
474
+ act(() => {
475
+ vi.advanceTimersByTime(100);
476
+ });
477
+ expect(onStop).toHaveBeenCalledOnce();
478
+ });
479
+ });
480
+
481
+ // -------------------------------------------------------------------------
482
+ // measure()
483
+ // -------------------------------------------------------------------------
484
+
485
+ describe("measure()", () => {
486
+ it("recalculates current scroll state on manual call", () => {
487
+ const el = makeEl({ scrollTop: 0 });
488
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
489
+
490
+ act(() => {
491
+ (el as any).scrollTop = 300;
492
+ result.current.measure();
493
+ });
494
+
495
+ expect(result.current.y$.get()).toBe(300);
496
+ });
497
+
498
+ it("all of x/y/arrivedState/directions are updated when measure() is called", () => {
499
+ // scrollHeight=1000, clientHeight=400 → maxY=600
500
+ const el = makeEl({ scrollTop: 0, scrollHeight: 1000, clientHeight: 400 });
501
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
502
+
503
+ act(() => {
504
+ (el as any).scrollTop = 600;
505
+ result.current.measure();
506
+ });
507
+
508
+ expect(result.current.y$.get()).toBe(600);
509
+ expect(result.current.arrivedState$.bottom.get()).toBe(true);
510
+ expect(result.current.directions$.bottom.get()).toBe(true);
511
+ });
512
+ });
513
+
514
+ // -------------------------------------------------------------------------
515
+ // throttle option
516
+ // -------------------------------------------------------------------------
517
+
518
+ describe("throttle option", () => {
519
+ it("measure is called on every scroll event when throttle is not set", () => {
520
+ const el = makeEl({ scrollTop: 0 });
521
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
522
+
523
+ act(() => {
524
+ (el as any).scrollTop = 100;
525
+ el.dispatchEvent(new Event("scroll"));
526
+ (el as any).scrollTop = 200;
527
+ el.dispatchEvent(new Event("scroll"));
528
+ });
529
+
530
+ expect(result.current.y$.get()).toBe(200);
531
+ });
532
+
533
+ it("consecutive calls within specified ms are ignored when throttle is set", () => {
534
+ vi.useRealTimers(); // real timers needed for Date.now()
535
+
536
+ const el = makeEl({ scrollTop: 0 });
537
+ const { result } = renderHook(() => useScroll(wrapEl(el), { throttle: 100 }));
538
+
539
+ act(() => {
540
+ (el as any).scrollTop = 50;
541
+ el.dispatchEvent(new Event("scroll"));
542
+ });
543
+ const firstY = result.current.y$.get();
544
+ expect(firstY).toBe(50);
545
+
546
+ act(() => {
547
+ // Second scroll within throttle window → skipped
548
+ (el as any).scrollTop = 80;
549
+ el.dispatchEvent(new Event("scroll"));
550
+ });
551
+ // y should remain 50 (throttled)
552
+ expect(result.current.y$.get()).toBe(50);
553
+ });
554
+ });
555
+
556
+ // -------------------------------------------------------------------------
557
+ // behavior by target type
558
+ // -------------------------------------------------------------------------
559
+
560
+ describe("behavior by target type", () => {
561
+ it("Window target — uses scrollX/scrollY", () => {
562
+ setWindowDimensions({ scrollX: 0, scrollY: 0 });
563
+ const { result } = renderHook(() => useScroll(window));
564
+
565
+ act(() => {
566
+ setWindowDimensions({ scrollY: 500 });
567
+ window.dispatchEvent(new Event("scroll"));
568
+ });
569
+
570
+ expect(result.current.y$.get()).toBe(500);
571
+ });
572
+
573
+ it("Document target — uses documentElement.scrollLeft/scrollTop", () => {
574
+ Object.defineProperty(document.documentElement, "scrollLeft", {
575
+ writable: true,
576
+ configurable: true,
577
+ value: 0,
578
+ });
579
+ Object.defineProperty(document.documentElement, "scrollTop", {
580
+ writable: true,
581
+ configurable: true,
582
+ value: 0,
583
+ });
584
+
585
+ const { result } = renderHook(() => useScroll(document));
586
+
587
+ act(() => {
588
+ Object.defineProperty(document.documentElement, "scrollTop", {
589
+ writable: true,
590
+ configurable: true,
591
+ value: 300,
592
+ });
593
+ document.dispatchEvent(new Event("scroll"));
594
+ });
595
+
596
+ expect(result.current.y$.get()).toBe(300);
597
+ });
598
+
599
+ it("HTMLElement target — uses scrollLeft/scrollTop", () => {
600
+ const el = makeEl({ scrollLeft: 0, scrollTop: 0 });
601
+ const { result } = renderHook(() => useScroll(wrapEl(el)));
602
+
603
+ act(() => {
604
+ (el as any).scrollTop = 150;
605
+ el.dispatchEvent(new Event("scroll"));
606
+ });
607
+
608
+ expect(result.current.y$.get()).toBe(150);
609
+ });
610
+ });
611
+
612
+ // -------------------------------------------------------------------------
613
+ // edge case: null target
614
+ // -------------------------------------------------------------------------
615
+
616
+ describe("edge case: null target", () => {
617
+ it("returns x=0, y=0 without error when null target is passed", () => {
618
+ const { result } = renderHook(() => useScroll(null));
619
+ expect(result.current.x$.get()).toBe(0);
620
+ expect(result.current.y$.get()).toBe(0);
621
+ });
622
+
623
+ it("arrivedState/directions maintain initial defaults when null target is passed", () => {
624
+ const { result } = renderHook(() => useScroll(null));
625
+ expect(result.current.arrivedState$.top.get()).toBe(true);
626
+ expect(result.current.arrivedState$.left.get()).toBe(true);
627
+ expect(result.current.arrivedState$.right.get()).toBe(false);
628
+ expect(result.current.arrivedState$.bottom.get()).toBe(false);
629
+ });
630
+
631
+ it("does not register scroll event listener when null target is passed", () => {
632
+ const addSpy = vi.spyOn(window, "addEventListener");
633
+ renderHook(() => useScroll(null));
634
+ const scrollCalls = addSpy.mock.calls.filter(([type]) => type === "scroll");
635
+ expect(scrollCalls).toHaveLength(0);
636
+ });
637
+ });
638
+
639
+ // -------------------------------------------------------------------------
640
+ // unmount / cleanup
641
+ // -------------------------------------------------------------------------
642
+
643
+ describe("unmount / cleanup", () => {
644
+ it("removes scroll event listener on unmount", async () => {
645
+ const el = makeEl();
646
+ const addSpy = vi.spyOn(el, "addEventListener");
647
+ const removeSpy = vi.spyOn(el, "removeEventListener");
648
+
649
+ const { unmount } = renderHook(() => useScroll(wrapEl(el)));
650
+ unmount();
651
+ await flush();
652
+
653
+ expect(addSpy.mock.calls.some(([type]) => type === "scroll")).toBe(true);
654
+ expect(removeSpy.mock.calls.some(([type]) => type === "scroll")).toBe(true);
655
+ });
656
+
657
+ it("cleans up pending idle timer on unmount", async () => {
658
+ const onStop = vi.fn();
659
+ const el = makeEl();
660
+ const { unmount } = renderHook(() =>
661
+ useScroll(wrapEl(el), { onStop, idle: 200 }),
662
+ );
663
+
664
+ act(() => {
665
+ el.dispatchEvent(new Event("scroll"));
666
+ });
667
+
668
+ unmount();
669
+ await flush();
670
+
671
+ act(() => {
672
+ vi.advanceTimersByTime(300);
673
+ });
674
+
675
+ expect(onStop).not.toHaveBeenCalled();
676
+ });
677
+ });
678
+ });