Files
fusion/packages/dashboard/app/components/RightDockExpandModal.tsx
gsxdsm ba24a530ef FN-8620: migrate dashboard modals to FloatingWindow
Standardize dashboard modal geometry and interaction behavior through the shared FloatingWindow contract.

- Migrate New Task, right-dock expansion, terminal, onboarding, and utility modals to FloatingWindow.
- Expand shared window resizing, positioning, accessibility, and contract coverage.
- Document the modal migration inventory and publish changesets.

Files changed:
 .changeset/fn-8607-floating-modal-contract.md      |   7 +
 .changeset/fn-8620-bespoke-geometry-modals.md      |   7 +
 docs/dashboard-guide.md                            |  47 ++
 docs/dashboard-modal-inventory.md                  |   6 +-
 .../app/components/AgentGenerationModal.tsx        |  14 +-
 .../dashboard/app/components/AgentImportModal.tsx  |   7 +-
 .../dashboard/app/components/AgentListModal.tsx    |   8 +-
 .../app/components/AgentOnboardingModal.tsx        |   6 +-
 .../app/components/DockerNodeOnboardingModal.tsx   |  14 +-
 .../ExperimentalAgentOnboardingModal.tsx           |   6 +-
 .../dashboard/app/components/FloatingWindow.css    |  21 +
 .../dashboard/app/components/FloatingWindow.tsx    |  62 ++-
 packages/dashboard/app/components/MailboxModal.tsx |  14 +-
 .../components/MilestoneSliceInterviewModal.tsx    |  12 +-
 .../app/components/NativeShellOnboardingModal.tsx  |   6 +-
 packages/dashboard/app/components/NewTaskModal.css | 196 +------
 packages/dashboard/app/components/NewTaskModal.tsx | 593 +++++++--------------
 packages/dashboard/app/components/RightDock.css    |  84 +--
 .../app/components/RightDockExpandModal.tsx        | 333 ++----------
 .../dashboard/app/components/SetupWizardModal.tsx  |  20 +-
 .../app/components/SubtaskBreakdownModal.tsx       |   6 +-
 .../dashboard/app/components/TerminalModal.css     | 120 +----
 .../dashboard/app/components/TerminalModal.tsx     | 321 ++---------
 .../__tests__/AgentModals.floatingWindow.test.tsx  |  26 +
 .../components/__tests__/FloatingWindow.test.tsx   |  27 +-
 .../app/components/__tests__/NewTaskModal.test.tsx |  66 +--
 .../OnboardingModals.floatingWindow.test.tsx       |  20 +
 .../app/components/__tests__/RightDock.test.tsx    |  18 +-
 .../components/__tests__/TerminalModal.test.tsx    |  61 +--
 .../UtilityModals.floatingWindow.test.tsx          |  18 +
 .../components/__tests__/migratedModalFixtures.tsx |  25 +
 .../__tests__/modalFloatingWindowContract.test.tsx |  30 ++
 32 files changed, 726 insertions(+), 1475 deletions(-)

Fusion-Task-Id: FN-8620
Fusion-Task-Lineage: 2a912260-3292-4b13-bcf0-9de5a3df8ccd
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-07-26 18:35:10 -07:00

103 lines
4.2 KiB
TypeScript

import { useCallback, useEffect, type RefObject } from "react";
import { Maximize2, X } from "lucide-react";
import { useTranslation } from "react-i18next";
import { FloatingWindow } from "./FloatingWindow";
import { findOverflowViewEntry, type OverflowViewEntry, type OverflowViewKey, type OverflowViewRenderProps, type OverflowViewVisibilityOptions } from "./overflowViewRegistry";
import "./RightDock.css";
const EXPAND_DEFAULT_WIDTH = 960;
const EXPAND_DEFAULT_HEIGHT = 600;
const EXPAND_MIN_WIDTH = 360;
const EXPAND_MIN_HEIGHT = 280;
type RenderableOverflowViewEntry = OverflowViewEntry & Required<Pick<OverflowViewEntry, "render">>;
export interface RightDockExpandModalProps {
viewKey: OverflowViewKey | null;
renderProps: OverflowViewRenderProps;
visibilityOptions?: OverflowViewVisibilityOptions;
onClose: () => void;
returnFocusRef?: RefObject<HTMLElement | null>;
}
/*
FNXC:Navigation 2026-06-21-00:00:
Expanded right-dock views reuse the same overflow registry render function as the dock body, so expanding changes only available space and never swaps to a divergent component or prop contract.
FNXC:Navigation 2026-06-21-20:16:
FN-6882 makes most right-dock entries launcher actions. The expand modal is restricted to inline view entries so action-only tools cannot open an empty modal body.
FNXC:i18n 2026-06-22-00:00:
Expanded right-dock modal affordance labels are accessibility copy and must use the app namespace so locale catalogs and fallback tests cover the modal surface with the dock controls.
*/
export function RightDockExpandModal({
viewKey,
renderProps,
visibilityOptions = {},
onClose,
returnFocusRef,
}: RightDockExpandModalProps) {
const { t } = useTranslation("app");
const resolvedEntry = viewKey ? findOverflowViewEntry(viewKey, visibilityOptions) : undefined;
const entry: RenderableOverflowViewEntry | undefined = resolvedEntry?.render ? { ...resolvedEntry, render: resolvedEntry.render } : undefined;
const closeAndRestoreFocus = useCallback(() => {
onClose();
window.setTimeout(() => returnFocusRef?.current?.focus(), 0);
}, [onClose, returnFocusRef]);
useEffect(() => {
if (entry) return undefined;
return () => {
returnFocusRef?.current?.focus();
};
}, [entry, returnFocusRef]);
if (!entry) return null;
const Icon = entry.icon;
const expandedViewLabel = t("rightDock.viewExpanded", "{{label}} expanded", { label: entry.label });
/*
FNXC:ModalTouchGeometry 2026-07-27-17:00:
FN-8620 replaces this pop-out's duplicate captured-pointer geometry engine with FloatingWindow.
Its custom header stays the delegated drag surface, while the shared host owns clamping, stack
claims, tablet touch targets, and teardown. The old size/position key pair intentionally resets
once to this single geometry record.
*/
return (
<FloatingWindow
title={expandedViewLabel}
onClose={closeAndRestoreFocus}
windowKey="right-dock-expand"
defaultSize={{ width: EXPAND_DEFAULT_WIDTH, height: EXPAND_DEFAULT_HEIGHT }}
minSize={{ width: EXPAND_MIN_WIDTH, height: EXPAND_MIN_HEIGHT }}
hideHeader
dragHandleSelector=".right-dock-expand-modal__header"
persistGeometryKey="fusion:right-dock-expand-modal-geometry"
suspendGeometryPersistenceOnMobile
suspendGeometryPersistenceOnShortViewport
ariaLabel={expandedViewLabel}
className="modal right-dock-expand-modal right-dock-expand-modal--floating"
testId="right-dock-expand-modal"
>
<div
className="modal-header right-dock-expand-modal__header right-dock-expand-modal__header--draggable"
data-testid="right-dock-expand-drag-handle"
>
<div className="right-dock-expand-modal__title">
<Maximize2 size={16} />
<Icon size={16} />
<span>{entry.label}</span>
</div>
<button className="modal-close" onClick={closeAndRestoreFocus} aria-label={t("rightDock.closeExpandedView", "Close expanded right dock view")} data-testid="right-dock-expand-close">
<X size={20} />
</button>
</div>
<div className="right-dock-expand-modal__body" data-testid="right-dock-expand-body">
{entry.render({ ...renderProps, surface: "expand" })}
</div>
</FloatingWindow>
);
}