From fd25aa03c57fb41a571b3fbf79a294b99ec72690 Mon Sep 17 00:00:00 2001
From: Chad Lavimoniere <clavimoniere@gitlab.com>
Date: Wed, 6 Dec 2023 22:12:06 +0000
Subject: [PATCH] Fix overflow issues for Roadmap

When system header or footer is turned on, Roadmap was having overflow
issues.

Changelog: fixed

EE: true
---
 .../roadmap/components/epics_list_section.vue        | 12 +++++++++++-
 .../roadmap/components/milestones_list_section.vue   |  1 +
 .../javascripts/roadmap/components/roadmap_shell.vue | 11 ++++++++++-
 ee/app/assets/stylesheets/page_bundles/roadmap.scss  |  4 ----
 .../roadmap/components/epics_list_section_spec.js    |  6 +++---
 5 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue b/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
index 3b8997df6bbe1..5df15ef791ffd 100644
--- a/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
+++ b/ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
@@ -70,9 +70,16 @@ export default {
     },
     sectionContainerStyles() {
       return {
+        height: `calc(100% - ${60 + this.milestonesHeight}px)`,
         width: `${EPIC_DETAILS_CELL_WIDTH + TIMELINE_CELL_MIN_WIDTH * this.timeframe.length}px`,
       };
     },
+    footerMessageHeight() {
+      return document.querySelector('.footer-message')?.getBoundingClientRect().height || 0;
+    },
+    milestonesHeight() {
+      return document.querySelector('.milestones-list-section')?.clientHeight || 0;
+    },
     epicsWithAssociatedParents() {
       return this.epics.filter((epic) => {
         if (epic.hasParent) {
@@ -93,6 +100,9 @@ export default {
       // Return epics with correct parent associations.
       return this.epicsWithAssociatedParents;
     },
+    scrollBarHeight() {
+      return this.$parent.$el.getBoundingClientRect().height - this.$parent.$el.clientHeight + 1;
+    },
   },
   mounted() {
     eventHub.$on('epicsListScrolled', this.handleEpicsListScroll);
@@ -141,7 +151,7 @@ export default {
         return {
           height: this.isScopedRoadmap
             ? `calc(${this.$root.$el.clientHeight}px - ${offsetTop}px)`
-            : `calc(100vh - ${top}px)`,
+            : `calc(100vh - ${top + this.scrollBarHeight + this.footerMessageHeight}px)`,
         };
       }
       return {};
diff --git a/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue b/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
index 842652158d821..39ebb03337771 100644
--- a/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
+++ b/ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
@@ -85,6 +85,7 @@ export default {
   mounted() {
     eventHub.$on('epicsListScrolled', this.handleEpicsListScroll);
     this.initMounted();
+    this.$emit('milestonesMounted');
   },
   beforeDestroy() {
     eventHub.$off('epicsListScrolled', this.handleEpicsListScroll);
diff --git a/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue b/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue
index c626181527f8e..ecab5c10d8a99 100644
--- a/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue
+++ b/ee/app/assets/javascripts/roadmap/components/roadmap_shell.vue
@@ -40,6 +40,7 @@ export default {
   data() {
     return {
       containerStyles: {},
+      canCalculateEpicsListHeight: false,
     };
   },
   computed: {
@@ -60,6 +61,9 @@ export default {
           return this.milestones;
       }
     },
+    footerMessageHeight() {
+      return document.querySelector('.footer-message')?.getBoundingClientRect().height || 0;
+    },
   },
   mounted() {
     if (this.isShowingMilestones) {
@@ -79,9 +83,12 @@ export default {
     getContainerStyles() {
       const { top } = this.$el.getBoundingClientRect();
       return {
-        height: this.isScopedRoadmap ? '100%' : `calc(100vh - ${top}px)`,
+        height: this.isScopedRoadmap ? '100%' : `calc(100vh - ${top + this.footerMessageHeight}px)`,
       };
     },
+    toggleCanCalculateEpicsListHeight() {
+      this.canCalculateEpicsListHeight = true;
+    },
   },
 };
 </script>
@@ -105,8 +112,10 @@ export default {
       :milestones="milestonesToShow"
       :timeframe="timeframe"
       :current-group-id="currentGroupId"
+      @milestonesMounted="toggleCanCalculateEpicsListHeight"
     />
     <epics-list-section
+      :key="canCalculateEpicsListHeight"
       :preset-type="presetType"
       :epics="epics"
       :timeframe="timeframe"
diff --git a/ee/app/assets/stylesheets/page_bundles/roadmap.scss b/ee/app/assets/stylesheets/page_bundles/roadmap.scss
index 5a807dfae25d1..a2a6e3a9128e7 100644
--- a/ee/app/assets/stylesheets/page_bundles/roadmap.scss
+++ b/ee/app/assets/stylesheets/page_bundles/roadmap.scss
@@ -200,10 +200,6 @@ html.group-epics-roadmap-html {
   }
 }
 
-.epics-list-section {
-  height: calc(100% - 60px);
-}
-
 .epics-list-item {
   &:hover {
     .epic-details-cell,
diff --git a/ee/spec/frontend/roadmap/components/epics_list_section_spec.js b/ee/spec/frontend/roadmap/components/epics_list_section_spec.js
index c5f6404e2ca3d..3ec0692095b1a 100644
--- a/ee/spec/frontend/roadmap/components/epics_list_section_spec.js
+++ b/ee/spec/frontend/roadmap/components/epics_list_section_spec.js
@@ -95,7 +95,7 @@ describe('EpicsListSectionComponent', () => {
 
     describe('sectionContainerStyles', () => {
       it('returns style string for container element based on sectionShellWidth', () => {
-        expect(wrapper.attributes('style')).toBe(
+        expect(wrapper.attributes('style')).toContain(
           `width: ${
             EPIC_DETAILS_CELL_WIDTH + TIMELINE_CELL_MIN_WIDTH * mockTimeframeMonths.length
           }px;`,
@@ -165,7 +165,7 @@ describe('EpicsListSectionComponent', () => {
       });
 
       it('sets style attribute containing `height` on empty row', () => {
-        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 0px);');
+        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 1px);');
       });
     });
 
@@ -177,7 +177,7 @@ describe('EpicsListSectionComponent', () => {
       });
 
       it('sets style attribute with `height` on empty row when there epics available to render', () => {
-        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 0px);');
+        expect(findEmptyRowEl().attributes('style')).toBe('height: calc(100vh - 1px);');
       });
     });
 
-- 
GitLab