@@ -108,7 +108,7 @@ class CFDRenderer extends UIControlsRenderer {
108108 [ this . width , this . focusHeight - this . margin . top + 1 ] ,
109109 ] )
110110 . on ( 'brush' , ( { selection } ) => {
111- this . selectedTimeRange = selection . map ( this . x . invert , this . x ) ;
111+ this . selectedTimeRange = selection . map ( this . x ? .invert , this . x ) ;
112112 this . updateGraph ( this . selectedTimeRange ) ;
113113 if ( this . isManualBrushUpdate && this . eventBus ) {
114114 this . eventBus ?. emitEvents ( `change-time-range-${ this . chartName } ` , this . selectedTimeRange ) ;
@@ -160,7 +160,7 @@ class CFDRenderer extends UIControlsRenderer {
160160 this . drawYAxis ( this . gy , this . currentYScale ) ;
161161
162162 this . chartArea
163- . selectAll ( 'path' )
163+ ? .selectAll ( 'path' )
164164 . attr ( 'class' , ( d ) => 'area ' + d . key )
165165 . style ( 'fill' , ( d ) => this . #statesColors( d . key ) )
166166 . attr ( 'd' , this . #createAreaGenerator( this . currentXScale , this . currentYScale ) ) ;
@@ -193,7 +193,7 @@ class CFDRenderer extends UIControlsRenderer {
193193 #drawArea( ) {
194194 this . chartArea = this . addClipPath ( this . svg , `${ this . chartName } -clip` ) ;
195195 this . chartArea
196- . append ( 'rect' )
196+ ? .append ( 'rect' )
197197 . attr ( 'width' , '100%' )
198198 . attr ( 'height' , '100%' )
199199 . attr ( 'id' , `${ this . chartName } -area` )
@@ -412,7 +412,7 @@ class CFDRenderer extends UIControlsRenderer {
412412 const triangleBase = 11 ;
413413 const trianglePath = `M${ - triangleBase / 2 } ,0 L${ triangleBase / 2 } ,0 L0,-${ triangleHeight } Z` ;
414414 this . chartArea
415- . selectAll ( 'observations' )
415+ ? .selectAll ( 'observations' )
416416 . data ( observations ?. data ?. filter ( ( d ) => d . chart_type === this . chartType ) )
417417 . join ( 'path' )
418418 . attr ( 'class' , 'observation-marker' )
@@ -436,14 +436,49 @@ class CFDRenderer extends UIControlsRenderer {
436436 */
437437 #showTooltipAndMovingLine( event ) {
438438 ! this . tooltip && this . #createTooltipAndMovingLine( event . lineX , event . lineY ) ;
439- const tooltipWidth = this . tooltip . node ( ) . getBoundingClientRect ( ) . width ;
440- const cfdGraphRect = d3 . select ( this . graphElementSelector ) . node ( ) . getBoundingClientRect ( ) ;
441- const tooltipTop = window . scrollY + cfdGraphRect . top - 50 ;
439+ let { tooltipWidth, tooltipTop } = this . computeTooltipWidthAndTop ( event ) ;
440+
442441 this . #clearTooltipAndMovingLine( event . lineX , event . lineY ) ;
443442 this . #positionTooltip( event . tooltipLeft , tooltipTop , tooltipWidth ) ;
444443 this . #populateTooltip( event ) ;
445444 }
446445
446+ computeTooltipWidthAndTop ( event ) {
447+ const tooltipRect = this . tooltip . node ( ) . getBoundingClientRect ( ) ;
448+ const tooltipWidth = tooltipRect . width ;
449+ const tooltipHeight = tooltipRect . height ;
450+ const graphRect = d3 . select ( this . graphElementSelector ) . node ( ) . getBoundingClientRect ( ) ;
451+ const padding = 10 ;
452+ let tooltipLeft = event . tooltipLeft ;
453+ let tooltipTop = event . lineY - tooltipHeight - padding ;
454+
455+ // Get viewport dimensions
456+ const viewportWidth = window . innerWidth ;
457+ const viewportHeight = window . innerHeight ;
458+
459+ // Adjust tooltipLeft to prevent overflow on the right
460+ if ( tooltipLeft + tooltipWidth + padding > viewportWidth ) {
461+ tooltipLeft = viewportWidth - tooltipWidth - padding ;
462+ }
463+
464+ // Adjust tooltipLeft to prevent overflow on the left
465+ if ( tooltipLeft < padding ) {
466+ tooltipLeft = padding ;
467+ }
468+
469+ // Adjust tooltipTop to prevent overflow on the top
470+ if ( tooltipTop < graphRect . top ) {
471+ // Position the tooltip below the event point if there's not enough space above
472+ tooltipTop = event . lineY + padding ;
473+
474+ // Ensure the tooltip doesn't overflow the bottom of the viewport
475+ if ( tooltipTop + tooltipHeight + padding > viewportHeight ) {
476+ tooltipTop = viewportHeight - tooltipHeight - padding ;
477+ }
478+ }
479+ return { tooltipWidth, tooltipTop } ;
480+ }
481+
447482 /**
448483 * Hides the tooltip and the moving line on the chart.
449484 */
@@ -460,9 +495,9 @@ class CFDRenderer extends UIControlsRenderer {
460495 * @private
461496 */
462497 #createTooltipAndMovingLine( x , y ) {
463- this . tooltip = d3 . select ( 'body' ) . append ( 'div' ) . attr ( 'class' , styles . tooltip ) . attr ( 'id' , 'c-tooltip' ) . style ( 'opacity' , 0 ) ;
498+ this . tooltip = d3 . select ( 'body' ) . append ( 'div' ) . attr ( 'class' , styles . chartTooltip ) . attr ( 'id' , 'c-tooltip' ) . style ( 'opacity' , 0 ) ;
464499 this . cfdLine = this . chartArea
465- . append ( 'line' )
500+ ? .append ( 'line' )
466501 . attr ( 'id' , `${ this . chartName } -line` )
467502 . attr ( 'stroke' , 'black' )
468503 . attr ( 'y1' , 0 )
@@ -481,7 +516,7 @@ class CFDRenderer extends UIControlsRenderer {
481516 */
482517 #positionTooltip( left , top , width ) {
483518 this . tooltip ?. transition ( ) . duration ( 100 ) . style ( 'opacity' , 0.9 ) . style ( 'pointer-events' , 'auto' ) ;
484- this . tooltip ?. style ( 'left' , left - width + 'px' ) . style ( 'top' , top + 30 + 'px' ) ;
519+ this . tooltip ?. style ( 'left' , left - width + 'px' ) . style ( 'top' , top + 'px' ) ;
485520 }
486521
487522 /**
@@ -492,7 +527,7 @@ class CFDRenderer extends UIControlsRenderer {
492527 #populateTooltip( event ) {
493528 this . tooltip ?. append ( 'p' ) . text ( formatDateToLocalString ( event . date ) ) . attr ( 'class' , 'text-center' ) ;
494529 const gridContainer = this . tooltip ?. append ( 'div' ) . attr ( 'class' , 'grid grid-cols-2' ) ;
495- if ( event . metrics . averageCycleTime > 0 ) {
530+ if ( event . metrics ? .averageCycleTime > 0 ) {
496531 gridContainer
497532 . append ( 'span' )
498533 . text ( 'Cycle time:' )
@@ -501,12 +536,12 @@ class CFDRenderer extends UIControlsRenderer {
501536 . style ( 'color' , this . #cycleTimeColor) ;
502537 gridContainer
503538 . append ( 'span' )
504- . text ( `${ event . metrics . averageCycleTime } days` )
539+ . text ( `${ event . metrics ? .averageCycleTime } days` )
505540 . attr ( 'class' , 'pl-1' )
506541 . style ( 'text-align' , 'start' )
507542 . style ( 'color' , this . #cycleTimeColor) ;
508543 }
509- if ( event . metrics . averageLeadTime > 0 ) {
544+ if ( event . metrics ? .averageLeadTime > 0 ) {
510545 gridContainer
511546 . append ( 'span' )
512547 . text ( 'Lead time:' )
@@ -566,8 +601,8 @@ class CFDRenderer extends UIControlsRenderer {
566601 return ; // Exit the function if metrics are already enabled
567602 }
568603 this . #areMetricsEnabled = true ;
569- this . chartArea . on ( 'mousemove' , ( event ) => this . #handleMouseEvent( event , `${ this . chartName } -mousemove` ) ) ;
570- this . chartArea . on ( 'click' , ( event ) => this . #handleMouseEvent( event , `${ this . chartName } -click` ) ) ;
604+ this . chartArea ? .on ( 'mousemove' , ( event ) => this . #handleMouseEvent( event , `${ this . chartName } -mousemove` ) ) ;
605+ this . chartArea ? .on ( 'click' , ( event ) => this . #handleMouseEvent( event , `${ this . chartName } -click` ) ) ;
571606 this . #setupMouseLeaveHandler( ) ;
572607 }
573608
@@ -590,8 +625,8 @@ class CFDRenderer extends UIControlsRenderer {
590625 return ;
591626 }
592627
593- const date = this . currentXScale . invert ( xPosition ) ;
594- const cumulativeCountOfWorkItems = this . currentYScale . invert ( yPosition ) ;
628+ const date = this . currentXScale ? .invert ( xPosition ) ;
629+ const cumulativeCountOfWorkItems = this . currentYScale ? .invert ( yPosition ) ;
595630 const excludeCycleTime = eventName . includes ( 'mousemove' ) && ! eventName . includes ( this . chartName ) ;
596631
597632 const metrics = this . computeMetrics ( date , Math . floor ( cumulativeCountOfWorkItems ) , excludeCycleTime ) ;
@@ -619,7 +654,7 @@ class CFDRenderer extends UIControlsRenderer {
619654 * @private
620655 */
621656 #setupMouseLeaveHandler( ) {
622- this . chartArea . on ( 'mouseleave' , ( ) => this . hideTooltipAndMovingLine ( ) ) ;
657+ this . chartArea ? .on ( 'mouseleave' , ( ) => this . hideTooltipAndMovingLine ( ) ) ;
623658 }
624659
625660 /**
@@ -729,8 +764,8 @@ class CFDRenderer extends UIControlsRenderer {
729764 * @private
730765 */
731766 #drawMetricLines( {
732- averageCycleTime,
733- averageLeadTime,
767+ averageCycleTime = '' ,
768+ averageLeadTime = '' ,
734769 leadTimeDateBefore,
735770 cycleTimeDateBefore,
736771 currentDate,
@@ -769,12 +804,12 @@ class CFDRenderer extends UIControlsRenderer {
769804 * @private
770805 */
771806 #drawHorizontalMetricLine( dateBefore , dateAfter , noOfItems , cssClass , color , width ) {
772- this . chartArea . selectAll ( `.${ cssClass } ` ) . remove ( ) ;
807+ this . chartArea ? .selectAll ( `.${ cssClass } ` ) . remove ( ) ;
773808 const x1 = this . currentXScale ( dateBefore ) ;
774809 const x2 = this . currentXScale ( dateAfter ) ;
775810 const y = this . currentYScale ( noOfItems ) ;
776811 this . chartArea
777- . append ( 'line' )
812+ ? .append ( 'line' )
778813 . attr ( 'x1' , x1 )
779814 . attr ( 'y1' , y )
780815 . attr ( 'x2' , x2 )
@@ -795,12 +830,12 @@ class CFDRenderer extends UIControlsRenderer {
795830 * @private
796831 */
797832 #drawVerticalMetricLine( date , noOfItemsBefore , noOfItemsAfter , cssClass , color ) {
798- this . chartArea . selectAll ( `.${ cssClass } ` ) . remove ( ) ;
833+ this . chartArea ? .selectAll ( `.${ cssClass } ` ) . remove ( ) ;
799834 const y1 = this . currentYScale ( noOfItemsBefore ) ;
800835 const y2 = this . currentYScale ( noOfItemsAfter ) ;
801836 const x = this . currentXScale ( date ) ;
802837 this . chartArea
803- . append ( 'line' )
838+ ? .append ( 'line' )
804839 . attr ( 'x1' , x )
805840 . attr ( 'y1' , y1 )
806841 . attr ( 'x2' , x )
@@ -815,9 +850,9 @@ class CFDRenderer extends UIControlsRenderer {
815850 * @private
816851 */
817852 #removeMetricsLines( ) {
818- this . chartArea . selectAll ( '.wip-line' ) . remove ( ) ;
819- this . chartArea . selectAll ( '.cycle-time-line' ) . remove ( ) ;
820- this . chartArea . selectAll ( '.lead-time-line' ) . remove ( ) ;
853+ this . chartArea ? .selectAll ( '.wip-line' ) . remove ( ) ;
854+ this . chartArea ? .selectAll ( '.cycle-time-line' ) . remove ( ) ;
855+ this . chartArea ? .selectAll ( '.lead-time-line' ) . remove ( ) ;
821856 }
822857
823858 /**
0 commit comments