FN-8633: improve tablet terminal dragging
Give floating tablet terminals a dedicated drag grip while preserving tab-strip panning. - add a touch-sized tablet-only header drag grip and pop-out hit target - preserve floating geometry at the tablet breakpoint and document the gesture - cover grip availability, dragging, and horizontal tab-panning CSS isolation Files changed: .changeset/fn-8633-tablet-terminal-drag.md | 7 ++ docs/dashboard-guide.md | 4 +- packages/dashboard/app/components/TerminalModal.css | 61 +++++++++++++ packages/dashboard/app/components/TerminalModal.tsx | 16 ++++ packages/dashboard/app/components/__tests__/TerminalModal.test.tsx | 99 ++++++++++++++++++++++ 5 files changed, 185 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-8633 Fusion-Task-Lineage: f1c442a8-3302-4f6a-98e9-f1efa4083c12 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-8633-tablet-terminal-drag.md
Normal file
7
.changeset/fn-8633-tablet-terminal-drag.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@runfusion/fusion": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
summary: Make floating terminal windows reliably draggable on touch tablets.
|
||||||
|
category: fix
|
||||||
|
dev: Adds a reserved tablet-only terminal header drag grip and touch-sized pop-out target.
|
||||||
@@ -817,7 +817,7 @@ Mailbox view shows inbox/outbox communication threads and unread state. When an
|
|||||||
|
|
||||||
## Interactive Terminal
|
## Interactive Terminal
|
||||||
|
|
||||||
Fusion embeds a terminal using xterm.js. Desktop and tablet use the footer status bar as the terminal launcher; mobile keeps the full-screen terminal path. Known touch tablets, including at the 768px responsive boundary, retain docked/floating presentation rather than falling back to the phone sheet. Their saved floating size and position and touch drag/edge-or-corner resize controls stay available when a software keyboard shortens the visual viewport. True narrow phones, including folded panes and short phone landscapes, intentionally remain full-screen.
|
Fusion embeds a terminal using xterm.js. Desktop and tablet use the footer status bar as the terminal launcher; mobile keeps the full-screen terminal path. Known touch tablets, including at the 768px responsive boundary, retain docked/floating presentation rather than falling back to the phone sheet. Their saved floating size and position and touch drag/edge-or-corner resize controls stay available when a software keyboard shortens the visual viewport. In tablet floating mode, use the reserved grip at the left side of the terminal header to move the window; it stays available even when the tab strip overflows. True narrow phones, including folded panes and short phone landscapes, intentionally remain full-screen.
|
||||||
|
|
||||||
<!-- FNXC:Terminal 2026-07-11-18:20: FN-7824 first-launch terminal sockets auto-retry with capped backoff until the first successful open, so the manual Reconnect affordance is reserved for terminal sessions that already connected and then exhaust their mid-session reconnect budget. -->
|
<!-- FNXC:Terminal 2026-07-11-18:20: FN-7824 first-launch terminal sockets auto-retry with capped backoff until the first successful open, so the manual Reconnect affordance is reserved for terminal sessions that already connected and then exhaust their mid-session reconnect budget. -->
|
||||||
On first launch or first open, the terminal keeps reconnecting automatically until its initial WebSocket opens; it should show **Reconnecting...** during that cold-start recovery rather than requiring a manual **Reconnect** click. If an already-connected terminal drops and exhausts its bounded reconnect budget, Fusion then parks it as **Disconnected** and surfaces the manual **Reconnect** control.
|
On first launch or first open, the terminal keeps reconnecting automatically until its initial WebSocket opens; it should show **Reconnecting...** during that cold-start recovery rather than requiring a manual **Reconnect** click. If an already-connected terminal drops and exhausts its bounded reconnect budget, Fusion then parks it as **Disconnected** and surfaces the manual **Reconnect** control.
|
||||||
@@ -838,7 +838,7 @@ Use the terminal on desktop/tablet:
|
|||||||
3. Drag the top edge of the docked or pinned panel.
|
3. Drag the top edge of the docked or pinned panel.
|
||||||
Expected outcome: the panel height changes within its viewport-safe bounds and persists per project, with pinned mode clamped shorter so the application remains usable.
|
Expected outcome: the panel height changes within its viewport-safe bounds and persists per project, with pinned mode clamped shorter so the application remains usable.
|
||||||
4. Select **Pop out** from the bottom action-control footer.
|
4. Select **Pop out** from the bottom action-control footer.
|
||||||
Expected outcome: the terminal switches to a floating window that can be dragged and freely resized; size, position, and display mode are saved per project.
|
Expected outcome: the terminal switches to a floating window that can be dragged and freely resized; size, position, and display mode are saved per project. On touch tablets, drag the reserved header grip rather than the horizontally scrollable tab strip.
|
||||||
5. Select **Dock** in the floating terminal.
|
5. Select **Dock** in the floating terminal.
|
||||||
Expected outcome: the terminal returns to the bottom docked overlay panel using the saved docked height.
|
Expected outcome: the terminal returns to the bottom docked overlay panel using the saved docked height.
|
||||||
6. Select the scripts chevron beside the footer **Terminal** button.
|
6. Select the scripts chevron beside the footer **Terminal** button.
|
||||||
|
|||||||
@@ -241,6 +241,67 @@ FN-7897: reserve space for the fixed ExecutorStatusBar footer when it is rendere
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:TerminalModalControls 2026-07-31-22:41:
|
||||||
|
The tablet classifier, rather than the legacy 769px media query, owns this grip so a known
|
||||||
|
768px touch tablet remains a floating window instead of receiving phone-sheet chrome. Only the
|
||||||
|
reserved grip disables browser gesture handling: applying `touch-action: none` to the panel or
|
||||||
|
header prevents native horizontal panning in the descendant overflowing tab strip.
|
||||||
|
*/
|
||||||
|
.modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-header__drag-grip {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-inline-size: var(--modal-resize-touch-target);
|
||||||
|
min-block-size: var(--modal-resize-touch-target);
|
||||||
|
touch-action: none;
|
||||||
|
cursor: grab;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-header__drag-grip::before {
|
||||||
|
content: "";
|
||||||
|
inline-size: var(--space-lg);
|
||||||
|
block-size: var(--space-xs);
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: color-mix(in srgb, var(--text-muted) 55%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-header__drag-grip:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:TerminalModalControls 2026-07-31-22:41:
|
||||||
|
The tab strip must retain native horizontal touch panning while the sibling grip owns window
|
||||||
|
movement. This explicit contract prevents future tablet drag rules from suppressing tab scrolling.
|
||||||
|
*/
|
||||||
|
.modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-tabs {
|
||||||
|
touch-action: pan-x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:TerminalModalControls 2026-07-31-22:19:
|
||||||
|
Tablets start docked, so the footer pop-out control needs the same touch-sized reach without
|
||||||
|
changing its established 14px icon or footer layout. Its pseudo-element only enlarges the button
|
||||||
|
hit area; it is not a second terminal control.
|
||||||
|
*/
|
||||||
|
.modal.terminal-modal.terminal-modal--tablet .terminal-clear-btn.terminal-clear-btn--icon[data-testid="terminal-popout-toggle"] {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal.terminal-modal.terminal-modal--tablet .terminal-clear-btn.terminal-clear-btn--icon[data-testid="terminal-popout-toggle"]::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inline-size: var(--modal-resize-touch-target);
|
||||||
|
block-size: var(--modal-resize-touch-target);
|
||||||
|
inset-inline-start: 50%;
|
||||||
|
inset-block-start: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-tab {
|
.terminal-tab {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -2426,12 +2426,18 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
FNXC:TerminalModalControls 2026-07-31-22:19:
|
||||||
|
The tab scroller owns tab presses and horizontal panning; floating-window drags begin only on
|
||||||
|
the reserved grip so a tab interaction cannot leave a captured header drag behind.
|
||||||
|
*/
|
||||||
const renderTerminalTabStrip = (measuring = false) => (
|
const renderTerminalTabStrip = (measuring = false) => (
|
||||||
<div
|
<div
|
||||||
ref={measuring || !tabsOverflow ? terminalTabsMeasureRef : undefined}
|
ref={measuring || !tabsOverflow ? terminalTabsMeasureRef : undefined}
|
||||||
className={`terminal-tabs${measuring ? " terminal-tabs--measuring" : ""}`}
|
className={`terminal-tabs${measuring ? " terminal-tabs--measuring" : ""}`}
|
||||||
data-testid={measuring ? "terminal-tabs-measuring" : "terminal-tabs"}
|
data-testid={measuring ? "terminal-tabs-measuring" : "terminal-tabs"}
|
||||||
aria-hidden={measuring || undefined}
|
aria-hidden={measuring || undefined}
|
||||||
|
onPointerDown={measuring ? undefined : (event) => event.stopPropagation()}
|
||||||
>
|
>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<div
|
<div
|
||||||
@@ -2541,6 +2547,16 @@ export function TerminalModal({ isOpen, onClose, initialCommand, initialCommandG
|
|||||||
{/* Header — on mobile (≤768px) use compact selector/actions;
|
{/* Header — on mobile (≤768px) use compact selector/actions;
|
||||||
.terminal-title is hidden; action button labels are hidden (icons only) */}
|
.terminal-title is hidden; action button labels are hidden (icons only) */}
|
||||||
<div className="terminal-header">
|
<div className="terminal-header">
|
||||||
|
{/*
|
||||||
|
FNXC:TerminalModalControls 2026-07-31-22:19:
|
||||||
|
Tablet floating terminals need a reserved, real pointer target because the flexing tab
|
||||||
|
strip otherwise consumes the entire delegated header handle. This plain element—not a
|
||||||
|
pseudo-element—is the testable drag target and remains outside the interactive-element
|
||||||
|
suppression filter in FloatingWindow.
|
||||||
|
*/}
|
||||||
|
{isTabletTerminal && !isMobileTerminal && !embedded && isFloatingMode && (
|
||||||
|
<div className="terminal-header__drag-grip" data-testid="terminal-drag-grip" aria-hidden="true" />
|
||||||
|
)}
|
||||||
{/* Tab Bar */}
|
{/* Tab Bar */}
|
||||||
{isMobileTerminal ? renderTerminalMobileTabs() : (
|
{isMobileTerminal ? renderTerminalMobileTabs() : (
|
||||||
<div className="terminal-tab-region" ref={terminalTabRegionRef}>
|
<div className="terminal-tab-region" ref={terminalTabRegionRef}>
|
||||||
|
|||||||
@@ -504,6 +504,7 @@ describe("TerminalModal", () => {
|
|||||||
expect(screen.queryByTestId("terminal-close-btn")).toBeNull();
|
expect(screen.queryByTestId("terminal-close-btn")).toBeNull();
|
||||||
expect(screen.queryByTestId("terminal-popout-toggle")).toBeNull();
|
expect(screen.queryByTestId("terminal-popout-toggle")).toBeNull();
|
||||||
expect(screen.queryByTestId("terminal-pin-toggle")).toBeNull();
|
expect(screen.queryByTestId("terminal-pin-toggle")).toBeNull();
|
||||||
|
expect(screen.queryByTestId("terminal-drag-grip")).toBeNull();
|
||||||
expect(screen.getByTestId("terminal-tabs")).toBeTruthy();
|
expect(screen.getByTestId("terminal-tabs")).toBeTruthy();
|
||||||
expect(mockUseTerminalSessions).toHaveBeenCalledWith("proj-123", {
|
expect(mockUseTerminalSessions).toHaveBeenCalledWith("proj-123", {
|
||||||
storageScope: "task:FN-7813",
|
storageScope: "task:FN-7813",
|
||||||
@@ -959,6 +960,7 @@ describe("TerminalModal", () => {
|
|||||||
|
|
||||||
const modal = await screen.findByTestId("terminal-modal");
|
const modal = await screen.findByTestId("terminal-modal");
|
||||||
expect(modal).toHaveClass("terminal-modal--docked");
|
expect(modal).toHaveClass("terminal-modal--docked");
|
||||||
|
expect(screen.queryByTestId("terminal-drag-grip")).toBeNull();
|
||||||
expect(screen.getByTestId("terminal-docked-resize-handle")).toBeInTheDocument();
|
expect(screen.getByTestId("terminal-docked-resize-handle")).toBeInTheDocument();
|
||||||
|
|
||||||
fireEvent.click(screen.getByTestId("terminal-popout-toggle"));
|
fireEvent.click(screen.getByTestId("terminal-popout-toggle"));
|
||||||
@@ -966,6 +968,7 @@ describe("TerminalModal", () => {
|
|||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId("terminal-modal")).toHaveClass("terminal-modal--floating");
|
expect(screen.getByTestId("terminal-modal")).toHaveClass("terminal-modal--floating");
|
||||||
expect(screen.getByTestId("terminal-modal")).not.toHaveClass("terminal-modal--docked");
|
expect(screen.getByTestId("terminal-modal")).not.toHaveClass("terminal-modal--docked");
|
||||||
|
expect(screen.queryByTestId("terminal-drag-grip")).toBeNull();
|
||||||
expect(screen.getByTestId("floating-window-resize-se")).toBeInTheDocument();
|
expect(screen.getByTestId("floating-window-resize-se")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1351,6 +1354,8 @@ describe("TerminalModal", () => {
|
|||||||
const modal = await screen.findByTestId("terminal-modal");
|
const modal = await screen.findByTestId("terminal-modal");
|
||||||
await waitFor(() => expect(mockTerminalInstance.open).toHaveBeenCalled());
|
await waitFor(() => expect(mockTerminalInstance.open).toHaveBeenCalled());
|
||||||
expect(modal).toHaveClass("terminal-modal--tablet", "terminal-modal--floating");
|
expect(modal).toHaveClass("terminal-modal--tablet", "terminal-modal--floating");
|
||||||
|
expect(modal).not.toHaveClass("terminal-modal--mobile");
|
||||||
|
expect(screen.getByTestId("terminal-drag-grip")).toBeInTheDocument();
|
||||||
// The 768px CSS fallback is full-screen only for true phones. A known
|
// The 768px CSS fallback is full-screen only for true phones. A known
|
||||||
// tablet must win that cascade with its stored floating geometry.
|
// tablet must win that cascade with its stored floating geometry.
|
||||||
const modalStyle = screen.getByTestId(`floating-window-terminal-${projectId}`).style;
|
const modalStyle = screen.getByTestId(`floating-window-terminal-${projectId}`).style;
|
||||||
@@ -1391,6 +1396,99 @@ describe("TerminalModal", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("gives tablet floating terminals a real touch drag grip without affecting other presentations", async () => {
|
||||||
|
const projectId = "tablet-terminal-drag-grip";
|
||||||
|
const previousInnerWidth = window.innerWidth;
|
||||||
|
const previousInnerHeight = window.innerHeight;
|
||||||
|
const previousScreen = Object.getOwnPropertyDescriptor(window, "screen");
|
||||||
|
const previousMaxTouchPoints = Object.getOwnPropertyDescriptor(navigator, "maxTouchPoints");
|
||||||
|
const setActiveTab = vi.fn();
|
||||||
|
const tabs = [
|
||||||
|
defaultTab,
|
||||||
|
{ ...defaultTab, id: "tab-2", title: "a deliberately long second terminal tab", isActive: false },
|
||||||
|
{ ...defaultTab, id: "tab-3", title: "a deliberately long third terminal tab", isActive: false },
|
||||||
|
];
|
||||||
|
Object.defineProperty(window, "innerWidth", { configurable: true, value: 1024 });
|
||||||
|
Object.defineProperty(window, "innerHeight", { configurable: true, value: 768 });
|
||||||
|
Object.defineProperty(window, "screen", { configurable: true, value: { width: 1024, height: 768 } });
|
||||||
|
Object.defineProperty(navigator, "maxTouchPoints", { configurable: true, value: 1 });
|
||||||
|
vi.spyOn(window, "matchMedia").mockImplementation((query: string) => ({
|
||||||
|
matches: query === "(min-width: 769px) and (max-width: 1024px)",
|
||||||
|
media: query,
|
||||||
|
onchange: null,
|
||||||
|
addListener: vi.fn(),
|
||||||
|
removeListener: vi.fn(),
|
||||||
|
addEventListener: vi.fn(),
|
||||||
|
removeEventListener: vi.fn(),
|
||||||
|
dispatchEvent: vi.fn(),
|
||||||
|
}));
|
||||||
|
window.localStorage.setItem(`fusion:terminal-display-mode-${projectId}`, "floating");
|
||||||
|
mockUseTerminalSessions.mockReturnValue({ ...defaultSessionState, tabs, activeTab: tabs[0], setActiveTab });
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { unmount } = render(<TerminalModal isOpen={true} onClose={mockOnClose} projectId={projectId} />);
|
||||||
|
const modal = await screen.findByTestId("terminal-modal");
|
||||||
|
const panel = screen.getByTestId(`floating-window-terminal-${projectId}`) as HTMLElement & {
|
||||||
|
setPointerCapture: (pointerId: number) => void;
|
||||||
|
releasePointerCapture: (pointerId: number) => void;
|
||||||
|
};
|
||||||
|
const grip = screen.getByTestId("terminal-drag-grip");
|
||||||
|
panel.setPointerCapture = vi.fn();
|
||||||
|
panel.releasePointerCapture = vi.fn();
|
||||||
|
|
||||||
|
expect(modal).toHaveClass("terminal-modal--tablet", "terminal-modal--floating");
|
||||||
|
expect(modal).not.toHaveClass("terminal-modal--mobile");
|
||||||
|
expect(grip).toHaveAttribute("aria-hidden", "true");
|
||||||
|
expect(grip.previousElementSibling).toBeNull();
|
||||||
|
expect(grip.tagName).toBe("DIV");
|
||||||
|
|
||||||
|
const initialLeft = panel.style.left;
|
||||||
|
const initialTop = panel.style.top;
|
||||||
|
fireEvent.pointerDown(grip, { pointerId: 63, pointerType: "touch", clientX: 100, clientY: 100 });
|
||||||
|
fireEvent.pointerMove(grip, { pointerId: 63, pointerType: "touch", clientX: 20, clientY: 20 });
|
||||||
|
fireEvent.pointerUp(grip, { pointerId: 63, pointerType: "touch" });
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(panel.setPointerCapture).toHaveBeenCalledWith(63);
|
||||||
|
expect(panel.style.left).not.toBe(initialLeft);
|
||||||
|
expect(panel.style.top).not.toBe(initialTop);
|
||||||
|
expect(JSON.parse(window.localStorage.getItem(`fusion:terminal-float-geometry-${projectId}`) ?? "{}").position).toEqual({ x: 16, y: 80 });
|
||||||
|
});
|
||||||
|
|
||||||
|
fireEvent.pointerDown(screen.getAllByRole("tab")[1], { pointerId: 64, pointerType: "touch" });
|
||||||
|
fireEvent.click(screen.getAllByRole("tab")[1]);
|
||||||
|
expect(setActiveTab).toHaveBeenCalledWith("tab-2");
|
||||||
|
expect(panel.setPointerCapture).toHaveBeenCalledTimes(1);
|
||||||
|
unmount();
|
||||||
|
} finally {
|
||||||
|
Object.defineProperty(window, "innerWidth", { configurable: true, value: previousInnerWidth });
|
||||||
|
Object.defineProperty(window, "innerHeight", { configurable: true, value: previousInnerHeight });
|
||||||
|
if (previousScreen) Object.defineProperty(window, "screen", previousScreen);
|
||||||
|
if (previousMaxTouchPoints) Object.defineProperty(navigator, "maxTouchPoints", previousMaxTouchPoints);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps the tablet drag grip isolated from the horizontally pannable tab strip", () => {
|
||||||
|
const gripSelector = ".modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-header__drag-grip";
|
||||||
|
const tabsSelector = ".modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-tabs";
|
||||||
|
const gripSelectorIndex = terminalModalCss.indexOf(gripSelector);
|
||||||
|
const gripRuleEnd = terminalModalCss.indexOf("}", gripSelectorIndex);
|
||||||
|
const gripRule = terminalModalCss.slice(gripSelectorIndex, gripRuleEnd);
|
||||||
|
const tabsSelectorIndex = terminalModalCss.indexOf(tabsSelector);
|
||||||
|
const tabsRuleEnd = terminalModalCss.indexOf("}", tabsSelectorIndex);
|
||||||
|
const tabsRule = terminalModalCss.slice(tabsSelectorIndex, tabsRuleEnd);
|
||||||
|
|
||||||
|
expect(gripSelectorIndex).toBeGreaterThan(-1);
|
||||||
|
expect(gripRule).toContain("min-block-size: var(--modal-resize-touch-target);");
|
||||||
|
expect(gripRule).toContain("min-inline-size: var(--modal-resize-touch-target);");
|
||||||
|
expect(gripRule).toContain("touch-action: none;");
|
||||||
|
expect(tabsSelectorIndex).toBeGreaterThan(-1);
|
||||||
|
expect(tabsRule).toContain("touch-action: pan-x;");
|
||||||
|
expect(terminalModalCss).not.toContain(".modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating {\n touch-action: none;");
|
||||||
|
expect(terminalModalCss).not.toContain(".modal.terminal-modal.terminal-modal--tablet.terminal-modal--floating .terminal-header {\n touch-action: none;");
|
||||||
|
expect(terminalModalCss.slice(0, gripSelectorIndex)).not.toContain("@media (min-width: 769px)");
|
||||||
|
expect(loadAllAppCss()).toContain(gripSelector);
|
||||||
|
});
|
||||||
|
|
||||||
it("keeps the floating terminal touch-draggable with theme-controlled shadow", () => {
|
it("keeps the floating terminal touch-draggable with theme-controlled shadow", () => {
|
||||||
const floatingWindowCss = readAppFile("components/FloatingWindow.css");
|
const floatingWindowCss = readAppFile("components/FloatingWindow.css");
|
||||||
expect(floatingWindowCss).toContain("box-shadow: var(--floating-window-shadow, var(--shadow-lg));");
|
expect(floatingWindowCss).toContain("box-shadow: var(--floating-window-shadow, var(--shadow-lg));");
|
||||||
@@ -1412,6 +1510,7 @@ describe("TerminalModal", () => {
|
|||||||
expect(modal).not.toHaveClass("terminal-modal--floating");
|
expect(modal).not.toHaveClass("terminal-modal--floating");
|
||||||
expect(screen.queryByTestId("terminal-docked-resize-handle")).toBeNull();
|
expect(screen.queryByTestId("terminal-docked-resize-handle")).toBeNull();
|
||||||
expect(screen.queryByTestId("terminal-popout-toggle")).toBeNull();
|
expect(screen.queryByTestId("terminal-popout-toggle")).toBeNull();
|
||||||
|
expect(screen.queryByTestId("terminal-drag-grip")).toBeNull();
|
||||||
expect(screen.queryByTestId("floating-window-resize-se")).toBeNull();
|
expect(screen.queryByTestId("floating-window-resize-se")).toBeNull();
|
||||||
} finally {
|
} finally {
|
||||||
Object.defineProperty(window, "innerWidth", { value: previousInnerWidth, configurable: true });
|
Object.defineProperty(window, "innerWidth", { value: previousInnerWidth, configurable: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user