@@ -11,10 +11,30 @@ const mockMap = {
1111 getCenter : jest . fn ( ( ) => ( { lat : 20 , lng : 0 } ) ) ,
1212 getZoom : jest . fn ( ( ) => 2 ) ,
1313 getContainer : jest . fn ( ( ) => document . getElementById ( 'chapter-map' ) ) ,
14+ dragging : {
15+ enable : jest . fn ( ) ,
16+ disable : jest . fn ( ) ,
17+ } ,
18+ touchZoom : {
19+ enable : jest . fn ( ) ,
20+ disable : jest . fn ( ) ,
21+ } ,
22+ doubleClickZoom : {
23+ enable : jest . fn ( ) ,
24+ disable : jest . fn ( ) ,
25+ } ,
1426 scrollWheelZoom : {
1527 enable : jest . fn ( ) ,
1628 disable : jest . fn ( ) ,
1729 } ,
30+ boxZoom : {
31+ enable : jest . fn ( ) ,
32+ disable : jest . fn ( ) ,
33+ } ,
34+ keyboard : {
35+ enable : jest . fn ( ) ,
36+ disable : jest . fn ( ) ,
37+ } ,
1838}
1939
2040const mockMarker = {
@@ -160,16 +180,20 @@ describe('ChapterMap', () => {
160180 expect ( mockTileLayer . addTo ) . toHaveBeenCalledWith ( mockMap )
161181 } )
162182
163- it ( 'creates and adds marker cluster group ' , ( ) => {
183+ it ( 'disables all interaction handlers on initialization ' , ( ) => {
164184 render ( < ChapterMap { ...defaultProps } /> )
165- expect ( L . markerClusterGroup ) . toHaveBeenCalled ( )
166- expect ( mockMap . addLayer ) . toHaveBeenCalledWith ( mockMarkerClusterGroup )
185+ expect ( mockMap . dragging . disable ) . toHaveBeenCalled ( )
186+ expect ( mockMap . touchZoom . disable ) . toHaveBeenCalled ( )
187+ expect ( mockMap . doubleClickZoom . disable ) . toHaveBeenCalled ( )
188+ expect ( mockMap . scrollWheelZoom . disable ) . toHaveBeenCalled ( )
189+ expect ( mockMap . boxZoom . disable ) . toHaveBeenCalled ( )
190+ expect ( mockMap . keyboard . disable ) . toHaveBeenCalled ( )
167191 } )
168192
169- it ( 'sets up event listeners for map interaction ' , ( ) => {
193+ it ( 'creates and adds marker cluster group ' , ( ) => {
170194 render ( < ChapterMap { ...defaultProps } /> )
171- expect ( mockMap . on ) . toHaveBeenCalledWith ( 'click' , expect . any ( Function ) )
172- expect ( mockMap . on ) . toHaveBeenCalledWith ( 'mouseout' , expect . any ( Function ) )
195+ expect ( L . markerClusterGroup ) . toHaveBeenCalled ( )
196+ expect ( mockMap . addLayer ) . toHaveBeenCalledWith ( mockMarkerClusterGroup )
173197 } )
174198 } )
175199
@@ -283,48 +307,35 @@ describe('ChapterMap', () => {
283307 expect ( getByText ( 'Unlock map' ) ) . toBeInTheDocument ( )
284308 } )
285309
286- it ( 'removes overlay when clicked' , ( ) => {
310+ it ( 'removes overlay when unlock button is clicked' , ( ) => {
287311 const { getByText, queryByText } = render ( < ChapterMap { ...defaultProps } /> )
288312
289- const overlay = getByText ( 'Unlock map' ) . closest ( 'button' )
290- fireEvent . click ( overlay ! )
313+ const button = getByText ( 'Unlock map' ) . closest ( 'button' )
314+ fireEvent . click ( button ! )
291315
292316 expect ( queryByText ( 'Unlock map' ) ) . not . toBeInTheDocument ( )
293317 } )
294318
295- it ( 'enables scroll wheel zoom when overlay is clicked' , ( ) => {
296- const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
297-
298- const overlay = getByText ( 'Unlock map' ) . closest ( 'button' )
299- fireEvent . click ( overlay ! )
300-
301- expect ( mockMap . scrollWheelZoom . enable ) . toHaveBeenCalled ( )
302- } )
303-
304- it ( 'handles keyboard interaction with Enter key' , ( ) => {
319+ it ( 'enables all interaction handlers when unlock button is clicked' , ( ) => {
305320 const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
306321
307- const overlay = getByText ( 'Unlock map' ) . closest ( 'button' )
308- fireEvent . keyDown ( overlay ! , { key : 'Enter' } )
309-
310- expect ( mockMap . scrollWheelZoom . enable ) . toHaveBeenCalled ( )
311- } )
312-
313- it ( 'handles keyboard interaction with Space key' , ( ) => {
314- const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
315-
316- const overlay = getByText ( 'Unlock map' ) . closest ( 'button' )
317- fireEvent . keyDown ( overlay ! , { key : ' ' } )
322+ const button = getByText ( 'Unlock map' ) . closest ( 'button' )
323+ fireEvent . click ( button ! )
318324
325+ expect ( mockMap . dragging . enable ) . toHaveBeenCalled ( )
326+ expect ( mockMap . touchZoom . enable ) . toHaveBeenCalled ( )
327+ expect ( mockMap . doubleClickZoom . enable ) . toHaveBeenCalled ( )
319328 expect ( mockMap . scrollWheelZoom . enable ) . toHaveBeenCalled ( )
329+ expect ( mockMap . boxZoom . enable ) . toHaveBeenCalled ( )
330+ expect ( mockMap . keyboard . enable ) . toHaveBeenCalled ( )
320331 } )
321332
322333 it ( 'has proper accessibility attributes' , ( ) => {
323334 const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
324335
325- const overlay = getByText ( 'Unlock map' ) . closest ( 'button' )
326- expect ( overlay ) . toHaveAttribute ( 'tabIndex ' , '0 ' )
327- expect ( overlay ) . toHaveAttribute ( 'aria-label' , 'Unlock map' )
336+ const button = getByText ( 'Unlock map' ) . closest ( 'button' )
337+ expect ( button ) . toHaveAttribute ( 'type ' , 'button ' )
338+ expect ( button ) . toHaveAttribute ( 'aria-label' , 'Unlock map' )
328339 } )
329340 } )
330341
@@ -427,8 +438,8 @@ describe('ChapterMap', () => {
427438 it ( 'shows zoom control when unlock button is clicked' , ( ) => {
428439 const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
429440
430- const overlay = getByText ( 'Unlock map' ) . closest ( 'button' )
431- fireEvent . click ( overlay )
441+ const button = getByText ( 'Unlock map' ) . closest ( 'button' )
442+ fireEvent . click ( button ! )
432443
433444 expect ( L . control . zoom ) . toHaveBeenCalledWith ( { position : 'topleft' } )
434445 expect ( mockZoomControl . addTo ) . toHaveBeenCalledWith ( mockMap )
@@ -468,4 +479,75 @@ describe('ChapterMap', () => {
468479 expect ( queryByLabelText ( / s h a r e l o c a t i o n / i) ) . not . toBeInTheDocument ( )
469480 } )
470481 } )
482+
483+ describe ( 'Escape Key Re-lock' , ( ) => {
484+ it ( 're-locks the map when Escape key is pressed' , ( ) => {
485+ const { getByText, queryByText } = render ( < ChapterMap { ...defaultProps } /> )
486+
487+ // First unlock the map
488+ const unlockButton = getByText ( 'Unlock map' )
489+ fireEvent . click ( unlockButton )
490+
491+ // Verify map is unlocked
492+ expect ( queryByText ( 'Unlock map' ) ) . not . toBeInTheDocument ( )
493+ expect ( mockMap . dragging . enable ) . toHaveBeenCalled ( )
494+
495+ // Press Escape to re-lock
496+ fireEvent . keyDown ( window , { key : 'Escape' } )
497+
498+ // Verify map is locked again
499+ expect ( getByText ( 'Unlock map' ) ) . toBeInTheDocument ( )
500+ expect ( mockMap . dragging . disable ) . toHaveBeenCalled ( )
501+ expect ( mockMap . scrollWheelZoom . disable ) . toHaveBeenCalled ( )
502+ } )
503+
504+ it ( 'does nothing when Escape is pressed and map is already locked' , ( ) => {
505+ const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
506+
507+ const disableCallsBefore = mockMap . dragging . disable . mock . calls . length
508+
509+ // Press Escape while map is locked
510+ fireEvent . keyDown ( window , { key : 'Escape' } )
511+
512+ // Should still show unlock button
513+ expect ( getByText ( 'Unlock map' ) ) . toBeInTheDocument ( )
514+
515+ // Disable should not be called again
516+ expect ( mockMap . dragging . disable . mock . calls . length ) . toBe ( disableCallsBefore )
517+ } )
518+
519+ it ( 'removes zoom control when Escape re-locks the map' , ( ) => {
520+ const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
521+
522+ // Unlock the map
523+ const unlockButton = getByText ( 'Unlock map' )
524+ fireEvent . click ( unlockButton )
525+
526+ // Zoom control should be added
527+ expect ( mockZoomControl . addTo ) . toHaveBeenCalled ( )
528+
529+ // Press Escape to re-lock
530+ fireEvent . keyDown ( window , { key : 'Escape' } )
531+
532+ // Zoom control should be removed
533+ expect ( mockZoomControl . remove ) . toHaveBeenCalled ( )
534+ } )
535+ } )
536+
537+ describe ( 'Pointer Events Structure' , ( ) => {
538+ it ( 'overlay wrapper has pointer-events-none class' , ( ) => {
539+ const { container } = render ( < ChapterMap { ...defaultProps } /> )
540+
541+ const overlay = container . querySelector ( '.pointer-events-none' )
542+ expect ( overlay ) . toBeInTheDocument ( )
543+ expect ( overlay ) . toHaveClass ( 'absolute' , 'inset-0' , 'z-[500]' )
544+ } )
545+
546+ it ( 'unlock button has pointer-events-auto class' , ( ) => {
547+ const { getByText } = render ( < ChapterMap { ...defaultProps } /> )
548+
549+ const button = getByText ( 'Unlock map' )
550+ expect ( button ) . toHaveClass ( 'pointer-events-auto' )
551+ } )
552+ } )
471553} )
0 commit comments