@@ -188,4 +188,126 @@ describe("useScatterChartZoom", () => {
188188 // Y domain should clamp to [0,70] (range 70 kept from [15,85])
189189 expect ( result . current . yDomain ) . toEqual ( [ 0 , 70 ] ) ;
190190 } ) ;
191+
192+ it ( "handleMouseMove returns early when not zoomed" , ( ) => {
193+ const { result } = renderHook ( ( ) => useScatterChartZoom ( data ) ) ;
194+
195+ // No zoom; handleMouseMove should be a no-op
196+ act ( ( ) => {
197+ result . current . handleMouseDown ( {
198+ clientX : 100 ,
199+ clientY : 100 ,
200+ } as React . MouseEvent ) ;
201+ result . current . handleMouseMove ( {
202+ clientX : 200 ,
203+ clientY : 150 ,
204+ } as React . MouseEvent ) ;
205+ } ) ;
206+
207+ expect ( result . current . xDomain ) . toEqual ( [ 0 , "auto" ] ) ;
208+ expect ( result . current . yDomain ) . toEqual ( [ 0 , 100 ] ) ;
209+ } ) ;
210+
211+ it ( "handleMouseMove does not pan when movement is within click threshold" , ( ) => {
212+ const { result } = renderHook ( ( ) => useScatterChartZoom ( data ) ) ;
213+
214+ ( result . current . containerRef as RefObject < HTMLDivElement | null > ) . current =
215+ {
216+ getBoundingClientRect : ( ) => rect ,
217+ } as HTMLDivElement ;
218+
219+ // Zoom in first
220+ act ( ( ) => {
221+ result . current . handleMouseDown ( {
222+ clientX : 500 ,
223+ clientY : 250 ,
224+ } as React . MouseEvent ) ;
225+ result . current . handleMouseUp ( {
226+ clientX : 500 ,
227+ clientY : 250 ,
228+ } as React . MouseEvent ) ;
229+ } ) ;
230+
231+ const xDomainAfterZoom = result . current . xDomain ;
232+ const yDomainAfterZoom = result . current . yDomain ;
233+
234+ // Move within CLICK_THRESHOLD (<=5 px) — should not pan
235+ act ( ( ) => {
236+ result . current . handleMouseDown ( {
237+ clientX : 100 ,
238+ clientY : 100 ,
239+ } as React . MouseEvent ) ;
240+ result . current . handleMouseMove ( {
241+ clientX : 103 ,
242+ clientY : 102 ,
243+ } as React . MouseEvent ) ;
244+ } ) ;
245+
246+ expect ( result . current . xDomain ) . toEqual ( xDomainAfterZoom ) ;
247+ expect ( result . current . yDomain ) . toEqual ( yDomainAfterZoom ) ;
248+ } ) ;
249+
250+ it ( "handleMouseUp returns early when containerRef is null after a clean mouseDown" , ( ) => {
251+ const { result } = renderHook ( ( ) => useScatterChartZoom ( data ) ) ;
252+
253+ // containerRef.current is null (default); click without moving should hit the null guard
254+ act ( ( ) => {
255+ result . current . handleMouseDown ( {
256+ clientX : 100 ,
257+ clientY : 100 ,
258+ } as React . MouseEvent ) ;
259+ result . current . handleMouseUp ( {
260+ clientX : 100 ,
261+ clientY : 100 ,
262+ } as React . MouseEvent ) ;
263+ } ) ;
264+
265+ // isZoomed stays false because the null guard prevented zoom logic
266+ expect ( result . current . isZoomed ) . toBe ( false ) ;
267+ } ) ;
268+
269+ it ( "pans using deltaScale fallback when containerRef getBoundingClientRect width is 0" , ( ) => {
270+ const { result } = renderHook ( ( ) => useScatterChartZoom ( data ) ) ;
271+
272+ // Use a real-size rect to zoom in
273+ ( result . current . containerRef as RefObject < HTMLDivElement | null > ) . current =
274+ {
275+ getBoundingClientRect : ( ) => rect ,
276+ } as HTMLDivElement ;
277+
278+ act ( ( ) => {
279+ result . current . handleMouseDown ( {
280+ clientX : 500 ,
281+ clientY : 250 ,
282+ } as React . MouseEvent ) ;
283+ result . current . handleMouseUp ( {
284+ clientX : 500 ,
285+ clientY : 250 ,
286+ } as React . MouseEvent ) ;
287+ } ) ;
288+
289+ expect ( result . current . xDomain ) . toEqual ( [ 15 , 85 ] ) ;
290+
291+ // Now set width to 0 so pixelWidth is falsy → deltaScale fallback
292+ ( result . current . containerRef as RefObject < HTMLDivElement | null > ) . current =
293+ {
294+ getBoundingClientRect : ( ) => ( { ...rect , width : 0 } ) ,
295+ } as HTMLDivElement ;
296+
297+ // Drag 1000px right: deltaScale=0.0005, scale=70
298+ // shift = 1000 * 0.0005 * 70 * 1 = 35 → [15-35, 85-35] = [-20, 50]
299+ // setXDomain clamps: rawStart=-20 → start=max(0,-20)=0; width=70; end=70 → [0,70]
300+ act ( ( ) => {
301+ result . current . handleMouseDown ( {
302+ clientX : 100 ,
303+ clientY : 100 ,
304+ } as React . MouseEvent ) ;
305+ result . current . handleMouseMove ( {
306+ clientX : 1100 ,
307+ clientY : 100 ,
308+ } as React . MouseEvent ) ;
309+ } ) ;
310+
311+ expect ( result . current . xDomain ) . toEqual ( [ 0 , 70 ] ) ;
312+ } ) ;
191313} ) ;
0 commit comments