diff --git a/galasa-ui/src/components/test-runs/saved-queries/CollapsibleSideBar.tsx b/galasa-ui/src/components/test-runs/saved-queries/CollapsibleSideBar.tsx index 8edd3351..ac592332 100644 --- a/galasa-ui/src/components/test-runs/saved-queries/CollapsibleSideBar.tsx +++ b/galasa-ui/src/components/test-runs/saved-queries/CollapsibleSideBar.tsx @@ -5,10 +5,11 @@ */ 'use client'; -import { useMemo, useState } from 'react'; -import { HeaderMenuButton, SideNavItems, Search, Button, InlineNotification } from '@carbon/react'; +import { useEffect, useMemo, useState } from 'react'; +import { HeaderMenuButton, Search, Button, InlineNotification } from '@carbon/react'; import { Add } from '@carbon/icons-react'; import styles from '@/styles/test-runs/saved-queries/CollapsibleSideBar.module.css'; +import testRunsPageStyles from '@/styles/test-runs/TestRunsPage.module.css'; import { arrayMove, SortableContext, @@ -57,6 +58,11 @@ export default function CollapsibleSideBar({ handleEditQueryName }: CollapsibleS // State to hold the data of the item currently being dragged for the DragOverlay const [activeQuery, setActiveQuery] = useState(null); + const [sideNavExpandedHeight, setSideNavExpandedHeight] = useState(0); + const [mainContentElement, setMainContentElement] = useState(null); + const SIDE_NAV_MIN_HEIGHT_PIXELS = 700; + const SIDE_NAV_HEIGHT_IF_NOT_RESIZABLE_PIXELS = 850; + // Isolate user-sortable queries from the default query const sortableQueries = useMemo( () => savedQueries.filter((query) => query.createdAt !== defaultQuery.createdAt), @@ -148,6 +154,51 @@ export default function CollapsibleSideBar({ handleEditQueryName }: CollapsibleS return sortableQueries; }, [searchTerm, sortableQueries]); + // Grab the main content element on page load. + useEffect(() => { + setMainContentElement(document.querySelector('.' + testRunsPageStyles.mainContent)); + }, []); + + useEffect(() => { + const updateSideNavHeight = () => { + if (mainContentElement) { + // As the mainContent for the test runs details is also flex, we must set this height to a minimum, wait a short while, then set the height of this element to the main content minus an offset. + setSideNavExpandedHeight(SIDE_NAV_MIN_HEIGHT_PIXELS); + setTimeout(() => { + // The .clientHeight seems to need mainContentElement checked inside the setTimeout(). + if (mainContentElement) { + const newHeight = mainContentElement.clientHeight - 50; + setSideNavExpandedHeight(newHeight); + } + }, 0); + } + }; + + // Initial update + updateSideNavHeight(); + + // Add event listener for main content resize. + const resizeObserver = new ResizeObserver((entries) => { + // Check if there's a valid entry. + if (entries[0]) { + updateSideNavHeight(); + } + }); + + if (mainContentElement) { + resizeObserver.observe(mainContentElement); + } else { + setSideNavExpandedHeight(SIDE_NAV_HEIGHT_IF_NOT_RESIZABLE_PIXELS); + } + + // Cleanup function to remove the event listener when the component unmounts + return () => { + if (mainContentElement) { + resizeObserver.unobserve(mainContentElement); + } + }; + }, [mainContentElement]); + return (
diff --git a/galasa-ui/src/styles/test-runs/TestRunsPage.module.css b/galasa-ui/src/styles/test-runs/TestRunsPage.module.css index 3b1af1d1..2ef84079 100644 --- a/galasa-ui/src/styles/test-runs/TestRunsPage.module.css +++ b/galasa-ui/src/styles/test-runs/TestRunsPage.module.css @@ -410,4 +410,3 @@ inset: 0; z-index: 1; } - diff --git a/galasa-ui/src/styles/test-runs/saved-queries/CollapsibleSideBar.module.css b/galasa-ui/src/styles/test-runs/saved-queries/CollapsibleSideBar.module.css index 1ff3e9d7..b2944dca 100644 --- a/galasa-ui/src/styles/test-runs/saved-queries/CollapsibleSideBar.module.css +++ b/galasa-ui/src/styles/test-runs/saved-queries/CollapsibleSideBar.module.css @@ -36,7 +36,6 @@ max-width: 360px; background-color: var(--cds-layer); border-right: 1px solid var(--cds-border-subtle); - height: 800px; overflow-y: auto; overflow-x: hidden; } diff --git a/galasa-ui/src/tests/components/test-runs/saved-queries/CollapsibleSideBar.test.tsx b/galasa-ui/src/tests/components/test-runs/saved-queries/CollapsibleSideBar.test.tsx index 1e30e472..6cdd91db 100644 --- a/galasa-ui/src/tests/components/test-runs/saved-queries/CollapsibleSideBar.test.tsx +++ b/galasa-ui/src/tests/components/test-runs/saved-queries/CollapsibleSideBar.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; import '@testing-library/jest-dom'; -import { fireEvent, render, screen } from '@testing-library/react'; +import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import CollapsibleSideBar from '@/components/test-runs/saved-queries/CollapsibleSideBar'; import { useSavedQueries } from '@/contexts/SavedQueriesContext'; import { useTestRunsQueryParams } from '@/contexts/TestRunsQueryParamsContext'; @@ -22,6 +22,9 @@ const mockQueries = [ { createdAt: '2023-01-02T00:00:00Z', title: 'Test Run 2', url: '' }, { createdAt: '2023-01-03T00:00:00Z', title: 'Test Run 3', url: '' }, ]; +let resizeObserverMock: jest.Mock; +let mockObserve: jest.Mock; +let mockUnobserve: jest.Mock; // Mock child components jest.mock('@/components/test-runs/saved-queries/QueryItem', () => ({ @@ -83,6 +86,19 @@ beforeEach(() => { defaultQuery: mockQueries[0], setSavedQueries: mockSetSavedQueries, })); + + resizeObserverMock = jest.fn().mockImplementation(() => { + mockObserve = jest.fn(); + mockUnobserve = jest.fn(); + const mockDisconnect = jest.fn(); + + return { + observe: mockObserve, + unobserve: mockUnobserve, + disconnect: mockDisconnect, + }; + }); + window.ResizeObserver = resizeObserverMock; }); describe('CollapsibleSideBar', () => { @@ -336,4 +352,33 @@ describe('CollapsibleSideBar', () => { expect(mockSetSavedQueries).not.toHaveBeenCalled(); }); }); + + describe('updating side nav height', () => { + test('should not observe the main content if main content not loaded', async () => { + render(); + expect(mockObserve).toHaveBeenCalledTimes(0); + }); + + test('should observe the main content if main content rendered, and set to height of main content -50px', async () => { + const mainContentElement = document.createElement('div'); + mainContentElement.className = 'mainContent'; + document.body.appendChild(mainContentElement); + + render(); + + const sidebar = screen.getByLabelText('Saved Queries Sidebar'); + + await waitFor(() => { + expect(mockObserve).toHaveBeenCalledTimes(1); + + if (sidebar) { + expect(sidebar.style.height).toBe('-50px'); + } else { + fail('could not find sidebar'); + } + + document.body.innerHTML = ''; + }); + }); + }); });