@@ -300,4 +300,138 @@ describe('Table of contents', function () {
300
300
expect ( scrollTopSpy ) . toHaveBeenCalledWith ( 399 )
301
301
} )
302
302
} )
303
+
304
+ describe ( 'Prevent table of contents open button overlapping focused elements' , function ( ) {
305
+ var _getBoundingClientRect
306
+ var _addEventListener
307
+ var _scrollTop
308
+ var $link
309
+ var $tocStickyHeader
310
+
311
+ beforeEach ( function ( ) {
312
+ _getBoundingClientRect = Element . prototype . getBoundingClientRect
313
+ _addEventListener = Document . prototype . addEventListener
314
+ _scrollTop = $ . fn . scrollTop
315
+
316
+ $tocStickyHeader = $ ( '.toc-show' )
317
+ $link = $ ( '<a href="">Test link</a>' )
318
+ $ ( 'body' ) . append ( $link )
319
+ } )
320
+
321
+ afterEach ( function ( ) {
322
+ Element . prototype . getBoundingClientRect = _getBoundingClientRect
323
+ Document . prototype . addEventListener = _addEventListener
324
+ $ . fn . extend ( { scrollTop : _scrollTop } )
325
+
326
+ $link . remove ( )
327
+ } )
328
+
329
+ it ( 'if an element is focused while being overlaped by the table of contents sticky header, the screen should scroll to reveal it' , function ( ) {
330
+ var tocStickyHeaderBottomPos = 50
331
+ var linkTopPos = 30
332
+ var windowScrollPos = 300
333
+ var scrollTopSpy = jasmine . createSpy ( 'scrollTop' )
334
+
335
+ $html . addClass ( 'mobile-size' ) // the open button only appears on mobile-size screens
336
+
337
+ module = new GOVUK . Modules . TableOfContents ( )
338
+ module . start ( $toc )
339
+
340
+ // stub DOM APIs used to work out if an element is overlaped
341
+ Element . prototype . getBoundingClientRect = function ( ) {
342
+ if ( this === $tocStickyHeader . get ( 0 ) ) {
343
+ return {
344
+ bottom : tocStickyHeaderBottomPos
345
+ }
346
+ }
347
+ if ( this === $link . get ( 0 ) ) {
348
+ return {
349
+ top : linkTopPos
350
+ }
351
+ }
352
+ }
353
+ $ . fn . scrollTop = function ( yPos ) {
354
+ if ( this . get ( 0 ) !== window ) { return _scrollTop ( arguments ) }
355
+ if ( yPos === undefined ) { // call for current scrollTop position
356
+ return windowScrollPos
357
+ } else {
358
+ scrollTopSpy ( yPos )
359
+ }
360
+ }
361
+
362
+ // replicating a real event requires us to focus the element and fire the event ourselves
363
+ $link . focus ( )
364
+ $link . get ( 0 ) . dispatchEvent ( new Event ( 'focus' ) )
365
+
366
+ expect ( scrollTopSpy ) . toHaveBeenCalledWith ( windowScrollPos - ( tocStickyHeaderBottomPos - linkTopPos ) )
367
+
368
+ $html . removeClass ( 'mobile-size' )
369
+ } )
370
+
371
+ it ( 'if the table of contents sticky header isn\'t shown, no focus tracking should happen' , function ( ) {
372
+ var scrollTopSpy = jasmine . createSpy ( 'scrollTop' )
373
+ var getBoundingClientRectSpy = jasmine . createSpy ( 'getBoundingClientRectSpy' )
374
+
375
+ module = new GOVUK . Modules . TableOfContents ( )
376
+ module . start ( $toc )
377
+
378
+ // stub out web APIs used if focus tracking runs
379
+ Element . prototype . getBoundingClientRect = function ( ) {
380
+ if ( ( this === $tocStickyHeader . get ( 0 ) ) || ( this === $link . get ( 0 ) ) ) {
381
+ getBoundingClientRectSpy ( )
382
+ return {
383
+ bottom : 50 ,
384
+ top : 30
385
+ }
386
+ }
387
+ }
388
+ $ . fn . scrollTop = function ( yPos ) {
389
+ if ( this . get ( 0 ) !== window ) { return _scrollTop ( arguments ) }
390
+ scrollTopSpy ( arguments )
391
+ }
392
+
393
+ // replicating a real event requires us to focus the element and fire the event ourselves
394
+ $link . focus ( )
395
+ $link . get ( 0 ) . dispatchEvent ( new Event ( 'focus' ) )
396
+
397
+ expect ( getBoundingClientRectSpy ) . not . toHaveBeenCalled ( )
398
+ expect ( scrollTopSpy ) . not . toHaveBeenCalled ( )
399
+ } )
400
+
401
+ it ( 'if the table of contents sticky header shows but then is hidden when the screen resizes, no focus tracking should happen' , function ( ) {
402
+ var scrollTopSpy = jasmine . createSpy ( 'scrollTop' )
403
+ var getBoundingClientRectSpy = jasmine . createSpy ( 'getBoundingClientRectSpy' )
404
+
405
+ $html . addClass ( 'mobile-size' ) // the open button only appears on mobile-size screens
406
+
407
+ module = new GOVUK . Modules . TableOfContents ( )
408
+ module . start ( $toc )
409
+
410
+ // simulate screen resizing to desktop-size
411
+ $html . removeClass ( 'mobile-size' )
412
+ $ ( window ) . trigger ( 'resize' )
413
+
414
+ // stub out web APIs used if focus tracking runs
415
+ Element . prototype . getBoundingClientRect = function ( ) {
416
+ if ( ( this === $tocStickyHeader . get ( 0 ) ) || ( this === $link . get ( 0 ) ) ) {
417
+ getBoundingClientRectSpy ( )
418
+ return {
419
+ bottom : 50 ,
420
+ top : 30
421
+ }
422
+ }
423
+ }
424
+ $ . fn . scrollTop = function ( yPos ) {
425
+ if ( this . get ( 0 ) !== window ) { return _scrollTop ( arguments ) }
426
+ scrollTopSpy ( arguments )
427
+ }
428
+
429
+ // replicating a real event requires us to focus the element and fire the event ourselves
430
+ $link . focus ( )
431
+ $link . get ( 0 ) . dispatchEvent ( new Event ( 'focus' ) )
432
+
433
+ expect ( getBoundingClientRectSpy ) . not . toHaveBeenCalled ( )
434
+ expect ( scrollTopSpy ) . not . toHaveBeenCalled ( )
435
+ } )
436
+ } )
303
437
} )
0 commit comments