Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions workspaces/homepage/.changeset/famous-bats-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@red-hat-developer-hub/backstage-plugin-dynamic-home-page': patch
---

fixes the responsiveness issue of the customizable home-page
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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';
Expand All @@ -50,6 +51,8 @@ export const CustomizableGridLayout = ({
homepageCards,
}: CustomizableGridLayoutProps) => {
const theme = useTheme();
const gridContainerRef = useRef<HTMLDivElement>(null);
useContainerQuery(gridContainerRef, { notifyWindowResize: true });

const config = useMemo(() => {
const defaultConfig: LayoutConfiguration[] = [];
Expand Down Expand Up @@ -90,16 +93,21 @@ export const CustomizableGridLayout = ({
},
}}
/>
<CustomHomepageGrid
config={config}
preventCollision={false}
compactType="vertical"
style={{ margin: '-10px' }}
<div
ref={gridContainerRef}
style={{ width: '100%', minWidth: 0, boxSizing: 'border-box' }}
>
{homepageCards.map((card, index) => (
<Fragment key={card.name ?? index}>{card.component}</Fragment>
))}
</CustomHomepageGrid>
<CustomHomepageGrid
config={config}
preventCollision={false}
compactType="vertical"
style={{ margin: '-10px' }}
>
{homepageCards.map((card, index) => (
<Fragment key={card.name ?? index}>{card.component}</Fragment>
))}
</CustomHomepageGrid>
</div>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -60,6 +61,8 @@ export interface CustomizableGridProps {
export const CustomizableGrid = ({ mountPoints }: CustomizableGridProps) => {
const theme = useTheme();
const { t } = useTranslation();
const gridContainerRef = useRef<HTMLDivElement>(null);
useContainerQuery(gridContainerRef, { notifyWindowResize: true });

const { children, config } = useMemo(() => {
// Children contains the additional / available cards a user can add.
Expand Down Expand Up @@ -153,14 +156,19 @@ export const CustomizableGrid = ({ mountPoints }: CustomizableGridProps) => {
},
}}
/>
<CustomHomepageGrid
config={config}
preventCollision={false}
compactType="vertical"
style={{ margin: '-10px' }}
<div
ref={gridContainerRef}
style={{ width: '100%', minWidth: 0, boxSizing: 'border-box' }}
>
{children}
</CustomHomepageGrid>
<CustomHomepageGrid
config={config}
preventCollision={false}
compactType="vertical"
style={{ margin: '-10px' }}
>
{children}
</CustomHomepageGrid>
</div>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -55,29 +64,41 @@

export const useContainerQuery = (
ref: RefObject<HTMLElement>,
options?: UseContainerQueryOptions,
): ContainerSize => {
const notifyWindowResize = options?.notifyWindowResize ?? false;
const [containerSize, setContainerSize] = useState<ContainerSize>('lg');

useLayoutEffect(() => {
const el = ref.current;
if (!el) return undefined;

const maybeNotifyWindowResize = () => {
if (notifyWindowResize) {
window.dispatchEvent(new Event('resize'));

Check warning on line 78 in workspaces/homepage/plugins/dynamic-home-page/src/hooks/useContainerQuery.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=redhat-developer_rhdh-plugins&issues=AZ1nHoAl4HXyV0LMG-8B&open=AZ1nHoAl4HXyV0LMG-8B&pullRequest=2709
}
};

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;
};
Loading