Fix infrastructure dialog scrolling

This commit is contained in:
rcourtman 2026-04-22 19:11:40 +01:00
parent 4d02f0769f
commit 2932822b60
7 changed files with 21 additions and 5 deletions

View file

@ -341,7 +341,11 @@ an add-only capacity posture.
slot; it must not bypass the probe endpoint or fabricate probe
candidates, and the agent credential slot must continue to reach
`InfrastructureInstallerSection.tsx` so install handoffs remain on the
canonical unified-agent install path. For PVE, PBS, and PMG, the
canonical unified-agent install path. Those governed add/edit dialogs
must also keep their form body scrollable inside the modal:
`InfrastructureWorkspace.tsx` and `ConnectionEditor.tsx` keep the
content shell on `min-h-0` flex columns so long lifecycle forms do not
clip lower fields behind the dialog boundary. For PVE, PBS, and PMG, the
credential slot is
`frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx`,
which reuses the existing `NodeModalBasicInfoSection`,

View file

@ -232,6 +232,11 @@ work extends shared components instead of creating new local variants.
helper plus runtime `monitored_system_capacity` reads rather than
reconstructing raw `current / limit` slash math or `0 remaining` copy in
the banner shell, state owner, or shared model.
Shared modal scroll containment follows that same owner split. The dialog
shell in `frontend-modern/src/components/shared/dialogModel.ts` must keep
shared panels `min-h-0`, and page-owned modal bodies may use
`overflow-y-auto` only under shrinkable flex columns instead of clipping
lower fields behind a fixed-height shell.
5. Keep shared infrastructure shell state on the reusable settings boundary: `frontend-modern/src/components/Settings/useSettingsInfrastructurePanelProps.ts` and `frontend-modern/src/components/Settings/InfrastructureWorkspace.tsx` must continue to derive provider counts, availability, and shared subtab copy from one infrastructure-settings source — via the unified aggregator through `frontend-modern/src/components/Settings/useConnectionsLedger.ts` — instead of creating provider-local summary fetches or VMware-only shell vocabulary. Phase 9 retired the old `PlatformConnectionsWorkspace` per-type shell, but setup guidance may still use `Platform connections` as the operator-facing label for the shared API-backed onboarding path.
That same shared shell boundary now owns the default posture for
`/settings/infrastructure`: the landing route should read as one
@ -245,6 +250,10 @@ work extends shared components instead of creating new local variants.
Those secondary views must stay under the same single `Infrastructure`
sidebar destination, but they may open in governed modal/dialog chrome when
that preserves the persistent source-manager page behind them.
That governed dialog chrome must also preserve inner form scrolling:
`InfrastructureWorkspace.tsx` and `ConnectionEditor.tsx` keep the add/edit
shell on `min-h-0` flex columns so long credential forms scroll inside the
modal body instead of trapping the lower fields below the fold.
That same shared shell boundary now owns one canonical infrastructure
destination in the Settings sidebar. `InfrastructureWorkspace.tsx` owns the
source-manager landing inside that destination, while route-backed add flows

View file

@ -114,7 +114,7 @@ export const ConnectionEditor: Component<ConnectionEditorProps> = (props) => {
);
return (
<div class="flex h-full flex-col">
<div class="flex h-full min-h-0 flex-col">
<Show
when={showCredentialSlot()}
fallback={

View file

@ -641,7 +641,7 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
ariaLabel={addDialogTitle()}
panelClass={isAgentDialog() ? 'max-w-6xl' : 'max-w-5xl'}
>
<div class="flex h-full flex-col">
<div class="flex h-full min-h-0 flex-col">
<div class="flex items-start justify-between gap-4 border-b border-border bg-surface-alt px-4 py-4 sm:px-6">
<div class="space-y-1">
<h2 class="text-base font-semibold text-base-content">{addDialogTitle()}</h2>
@ -712,7 +712,7 @@ const InfrastructureWorkspaceContent: Component<InfrastructureWorkspaceProps> =
ariaLabel={editDialogTitle()}
panelClass={connection.type === 'agent' ? 'max-w-5xl' : 'max-w-5xl'}
>
<div class="flex h-full flex-col">
<div class="flex h-full min-h-0 flex-col">
<div class="flex items-start justify-between gap-4 border-b border-border bg-surface-alt px-4 py-4 sm:px-6">
<div class="space-y-1">
<h2 class="text-base font-semibold text-base-content">{editDialogTitle()}</h2>

View file

@ -112,6 +112,7 @@ describe('settings architecture guardrails', () => {
expect(infrastructureWorkspaceSource).toContain('<InfrastructureSourceManager');
expect(infrastructureWorkspaceSource).toContain('<InfrastructureSourcePicker');
expect(infrastructureWorkspaceSource).not.toContain('<ConnectionsTable rows={rows} />');
expect(infrastructureWorkspaceSource).toContain('flex h-full min-h-0 flex-col');
expect(infrastructureWorkspaceSource).toContain("showSlotHeader={false}");
expect(infrastructureWorkspaceSource).toContain(
"trackInitialCatalogSelection={activeAddType() !== 'agent'}",
@ -146,6 +147,7 @@ describe('settings architecture guardrails', () => {
expect(connectionEditorSource).toContain('<AddressProbeStep');
expect(connectionEditorSource).toContain('Detect from address');
expect(connectionEditorSource).toContain('Address probe');
expect(connectionEditorSource).toContain('flex h-full min-h-0 flex-col');
expect(connectionEditorSource).toContain('Back to source types');
expect(connectionEditorSource).toContain('Back to detect');
expect(connectionEditorSource).toContain('What happens next');

View file

@ -50,6 +50,7 @@ describe('Dialog', () => {
));
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveClass('min-h-0');
const backdrop = document.querySelector('[data-dialog-backdrop]') as HTMLElement | null;
expect(backdrop).not.toBeNull();
if (!backdrop) return;

View file

@ -29,7 +29,7 @@ export function getDialogAlignmentClass(layout: DialogLayout): string {
}
export function getDialogPanelClass(layout: DialogLayout, panelClass?: string): string {
return `relative flex w-full flex-col overflow-hidden bg-surface border border-border outline-none pointer-events-auto ${
return `relative flex min-h-0 w-full flex-col overflow-hidden bg-surface border border-border outline-none pointer-events-auto ${
layout === 'drawer-right'
? 'h-dvh max-w-[720px] rounded-none border-y-0 border-r-0 animate-slide-up sm:h-full sm:max-h-dvh sm:rounded-l-xl sm:border-y sm:border-r-0'
: 'max-h-[calc(100dvh-2rem)] rounded-md animate-slide-up'