diff --git a/workspaces/homepage/.changeset/famous-bats-whisper.md b/workspaces/homepage/.changeset/famous-bats-whisper.md new file mode 100644 index 0000000000..1c165b3395 --- /dev/null +++ b/workspaces/homepage/.changeset/famous-bats-whisper.md @@ -0,0 +1,5 @@ +--- +'@red-hat-developer-hub/backstage-plugin-dynamic-home-page': patch +--- + +fixes the responsiveness issue of the customizable home-page diff --git a/workspaces/homepage/plugins/dynamic-home-page/src/alpha/components/CustomizableGridLayout.tsx b/workspaces/homepage/plugins/dynamic-home-page/src/alpha/components/CustomizableGridLayout.tsx index c8b650f3d4..3ed6593374 100644 --- a/workspaces/homepage/plugins/dynamic-home-page/src/alpha/components/CustomizableGridLayout.tsx +++ b/workspaces/homepage/plugins/dynamic-home-page/src/alpha/components/CustomizableGridLayout.tsx @@ -20,7 +20,7 @@ // https://github.com/backstage/backstage/blob/master/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx // but without the drag and drop functionality. -import { Fragment, useMemo } from 'react'; +import { Fragment, useMemo, useRef } from 'react'; import { CustomHomepageGrid, @@ -29,6 +29,7 @@ import { import { useTheme } from '@mui/material/styles'; import GlobalStyles from '@mui/material/GlobalStyles'; import { HomePageCardConfig } from '../../types'; +import { useContainerQuery } from '../../hooks/useContainerQuery'; import 'react-grid-layout/css/styles.css'; import { isCardADefaultConfiguration } from '../utils'; @@ -50,6 +51,8 @@ export const CustomizableGridLayout = ({ homepageCards, }: CustomizableGridLayoutProps) => { const theme = useTheme(); + const gridContainerRef = useRef(null); + useContainerQuery(gridContainerRef, { notifyWindowResize: true }); const config = useMemo(() => { const defaultConfig: LayoutConfiguration[] = []; @@ -90,16 +93,21 @@ export const CustomizableGridLayout = ({ }, }} /> - - {homepageCards.map((card, index) => ( - {card.component} - ))} - + + {homepageCards.map((card, index) => ( + {card.component} + ))} + + ); }; diff --git a/workspaces/homepage/plugins/dynamic-home-page/src/components/CustomizableGrid.tsx b/workspaces/homepage/plugins/dynamic-home-page/src/components/CustomizableGrid.tsx index edb41a2b85..deaf5233be 100644 --- a/workspaces/homepage/plugins/dynamic-home-page/src/components/CustomizableGrid.tsx +++ b/workspaces/homepage/plugins/dynamic-home-page/src/components/CustomizableGrid.tsx @@ -21,7 +21,7 @@ // but without the drag and drop functionality. import type { ReactElement } from 'react'; -import { useMemo } from 'react'; +import { useMemo, useRef } from 'react'; import { CustomHomepageGrid, @@ -41,6 +41,7 @@ import 'react-grid-layout/css/styles.css'; import { HomePageCardMountPoint } from '../types'; import { dynamicHomePagePlugin } from '../plugin'; import { useTranslation } from '../hooks/useTranslation'; +import { useContainerQuery } from '../hooks/useContainerQuery'; import { isCardADefaultConfiguration, getCardTitle, @@ -60,6 +61,8 @@ export interface CustomizableGridProps { export const CustomizableGrid = ({ mountPoints }: CustomizableGridProps) => { const theme = useTheme(); const { t } = useTranslation(); + const gridContainerRef = useRef(null); + useContainerQuery(gridContainerRef, { notifyWindowResize: true }); const { children, config } = useMemo(() => { // Children contains the additional / available cards a user can add. @@ -153,14 +156,19 @@ export const CustomizableGrid = ({ mountPoints }: CustomizableGridProps) => { }, }} /> - - {children} - + + {children} + + ); }; diff --git a/workspaces/homepage/plugins/dynamic-home-page/src/hooks/useContainerQuery.ts b/workspaces/homepage/plugins/dynamic-home-page/src/hooks/useContainerQuery.ts index 4bfedaef99..f1c885eb01 100644 --- a/workspaces/homepage/plugins/dynamic-home-page/src/hooks/useContainerQuery.ts +++ b/workspaces/homepage/plugins/dynamic-home-page/src/hooks/useContainerQuery.ts @@ -27,10 +27,19 @@ * - Uses ResizeObserver to track width changes * - Maps the width to MUI-like breakpoints * - Updates state only when the breakpoint actually changes + * + * Optional `notifyWindowResize`: when true, also dispatches a `window` `resize` + * event on each observed width change. That lets react-grid-layout's + * `WidthProvider` (used by `CustomHomepageGrid`) remeasure when the main column + * shrinks without a viewport resize (e.g. RHDH docked drawer). * */ import { useLayoutEffect, useState, RefObject } from 'react'; +export type UseContainerQueryOptions = { + notifyWindowResize?: boolean; +}; + // Container Breakpoints (MUI-like, but container-based) export type ContainerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; @@ -55,29 +64,41 @@ const resolveContainerSize = (width: number): ContainerSize => { export const useContainerQuery = ( ref: RefObject, + options?: UseContainerQueryOptions, ): ContainerSize => { + const notifyWindowResize = options?.notifyWindowResize ?? false; const [containerSize, setContainerSize] = useState('lg'); useLayoutEffect(() => { const el = ref.current; if (!el) return undefined; + const maybeNotifyWindowResize = () => { + if (notifyWindowResize) { + window.dispatchEvent(new Event('resize')); + } + }; + const updateSize = (width: number) => { const next = resolveContainerSize(width); setContainerSize(prev => (prev === next ? prev : next)); }; - // Initial read - updateSize(el.getBoundingClientRect().width); + const onObservedWidth = (width: number) => { + updateSize(width); + maybeNotifyWindowResize(); + }; + + onObservedWidth(el.getBoundingClientRect().width); const observer = new ResizeObserver(entries => { if (!entries.length) return; - updateSize(entries[0].contentRect.width); + onObservedWidth(entries[0].contentRect.width); }); observer.observe(el); return () => observer.disconnect(); - }, [ref]); + }, [ref, notifyWindowResize]); return containerSize; };