feat(FN-2612): merge fusion/fn-2612 (auto-resolved)
- test(FN-2612): complete Step 4 — cover shortcut helpers and panel interactions - feat(FN-2612): complete Step 3 — style shortcut panel controls - feat(FN-2612): complete Step 2 — add terminal shortcut panel UI - feat(FN-2612): complete Step 1 — add control sequence helpers
This commit is contained in:
@@ -621,6 +621,61 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
padding: var(--space-xs) var(--space-sm);
|
||||||
|
background: var(--surface);
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
max-height: calc(var(--space-2xl) + var(--space-xl) + var(--space-lg));
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-modifier-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: calc(var(--space-xl) + var(--space-xs));
|
||||||
|
min-height: calc(var(--space-xl) + var(--space-xs));
|
||||||
|
padding: 0 var(--space-xs);
|
||||||
|
background: var(--card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
color: var(--text);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-btn:hover {
|
||||||
|
background: var(--card-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-btn:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
box-shadow: var(--focus-ring-strong);
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-btn--modifier {
|
||||||
|
min-width: calc(var(--space-xl) + var(--space-md) + var(--space-xs));
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-btn--modifier.is-active {
|
||||||
|
background: var(--in-progress);
|
||||||
|
color: var(--card);
|
||||||
|
border-color: var(--in-progress);
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-status-bar {
|
.terminal-status-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -877,6 +932,17 @@
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-panel {
|
||||||
|
max-height: calc(var(--space-2xl) + var(--space-2xl) + var(--space-2xl) + var(--space-xl));
|
||||||
|
}
|
||||||
|
|
||||||
|
.terminal-shortcut-btn,
|
||||||
|
.terminal-shortcut-btn--modifier {
|
||||||
|
min-width: calc(var(--space-xl) + var(--space-md));
|
||||||
|
min-height: calc(var(--space-xl) + var(--space-md));
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
.terminal-font-size-btn {
|
.terminal-font-size-btn {
|
||||||
min-width: calc(var(--space-xl) + var(--space-md));
|
min-width: calc(var(--space-xl) + var(--space-md));
|
||||||
min-height: calc(var(--space-xl) + var(--space-md));
|
min-height: calc(var(--space-xl) + var(--space-md));
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import "./TerminalModal.css";
|
import "./TerminalModal.css";
|
||||||
import { useState, useEffect, useRef, useCallback } from "react";
|
import { useState, useEffect, useRef, useCallback } from "react";
|
||||||
import { getErrorMessage } from "@fusion/core";
|
import { getErrorMessage } from "@fusion/core";
|
||||||
import { X, Trash2, Terminal as TerminalIcon, RefreshCw, Minus, Plus } from "lucide-react";
|
import {
|
||||||
|
X,
|
||||||
|
Trash2,
|
||||||
|
Terminal as TerminalIcon,
|
||||||
|
RefreshCw,
|
||||||
|
Minus,
|
||||||
|
Plus,
|
||||||
|
Keyboard,
|
||||||
|
} from "lucide-react";
|
||||||
import { useTerminal } from "../hooks/useTerminal";
|
import { useTerminal } from "../hooks/useTerminal";
|
||||||
import { useTerminalSessions } from "../hooks/useTerminalSessions";
|
import { useTerminalSessions } from "../hooks/useTerminalSessions";
|
||||||
import "@xterm/xterm/css/xterm.css";
|
import "@xterm/xterm/css/xterm.css";
|
||||||
@@ -18,6 +26,48 @@ const DEFAULT_FONT_SIZE = 14;
|
|||||||
const MIN_TERMINAL_FONT_SIZE = 8;
|
const MIN_TERMINAL_FONT_SIZE = 8;
|
||||||
const MAX_TERMINAL_FONT_SIZE = 32;
|
const MAX_TERMINAL_FONT_SIZE = 32;
|
||||||
|
|
||||||
|
export function ctrlChar(key: string): string {
|
||||||
|
if (!key) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalized = key.slice(0, 1).toUpperCase();
|
||||||
|
|
||||||
|
if (normalized === "[") {
|
||||||
|
return "\x1b";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized >= "A" && normalized <= "Z") {
|
||||||
|
return String.fromCharCode(normalized.charCodeAt(0) - 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function altChar(key: string): string {
|
||||||
|
return `\x1b${key}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ShortcutKey {
|
||||||
|
label: string;
|
||||||
|
key: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SHORTCUT_KEYS: ShortcutKey[] = [
|
||||||
|
{ label: "C", key: "c", description: "SigInt" },
|
||||||
|
{ label: "D", key: "d", description: "EOF" },
|
||||||
|
{ label: "Z", key: "z", description: "Suspend" },
|
||||||
|
{ label: "L", key: "l", description: "Clear" },
|
||||||
|
{ label: "R", key: "r", description: "Reverse search" },
|
||||||
|
{ label: "A", key: "a", description: "Home" },
|
||||||
|
{ label: "E", key: "e", description: "End" },
|
||||||
|
{ label: "U", key: "u", description: "Kill line" },
|
||||||
|
{ label: "K", key: "k", description: "Kill to EOL" },
|
||||||
|
{ label: "W", key: "w", description: "Del word" },
|
||||||
|
{ label: ".", key: ".", description: "Last argument" },
|
||||||
|
];
|
||||||
|
|
||||||
function clampTerminalFontSize(value: number): number {
|
function clampTerminalFontSize(value: number): number {
|
||||||
return Math.min(MAX_TERMINAL_FONT_SIZE, Math.max(MIN_TERMINAL_FONT_SIZE, value));
|
return Math.min(MAX_TERMINAL_FONT_SIZE, Math.max(MIN_TERMINAL_FONT_SIZE, value));
|
||||||
}
|
}
|
||||||
@@ -183,6 +233,8 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
|||||||
const [keyboardOverlap, setKeyboardOverlap] = useState(0);
|
const [keyboardOverlap, setKeyboardOverlap] = useState(0);
|
||||||
const [viewportHeight, setViewportHeight] = useState<number | null>(null);
|
const [viewportHeight, setViewportHeight] = useState<number | null>(null);
|
||||||
const [fontSize, setFontSize] = useState<number>(() => readInitialTerminalFontSize());
|
const [fontSize, setFontSize] = useState<number>(() => readInitialTerminalFontSize());
|
||||||
|
const [showShortcuts, setShowShortcuts] = useState(false);
|
||||||
|
const [stickyModifier, setStickyModifier] = useState<null | "ctrl" | "alt">(null);
|
||||||
|
|
||||||
const terminalRef = useRef<HTMLDivElement>(null);
|
const terminalRef = useRef<HTMLDivElement>(null);
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -618,6 +670,8 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
|||||||
hasInitialCommandRun.current = false;
|
hasInitialCommandRun.current = false;
|
||||||
setError(null);
|
setError(null);
|
||||||
setExitCode(null);
|
setExitCode(null);
|
||||||
|
setShowShortcuts(false);
|
||||||
|
setStickyModifier(null);
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
// Subscribe to terminal data.
|
// Subscribe to terminal data.
|
||||||
@@ -906,6 +960,37 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
|||||||
refitTerminal();
|
refitTerminal();
|
||||||
}, [refitTerminal]);
|
}, [refitTerminal]);
|
||||||
|
|
||||||
|
const toggleModifier = useCallback((modifier: "ctrl" | "alt") => {
|
||||||
|
setStickyModifier((current) => (current === modifier ? null : modifier));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const sendShortcutKey = useCallback(
|
||||||
|
(key: string) => {
|
||||||
|
if (stickyModifier === "ctrl") {
|
||||||
|
sendInput(ctrlChar(key));
|
||||||
|
setStickyModifier(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stickyModifier === "alt") {
|
||||||
|
sendInput(altChar(key));
|
||||||
|
setStickyModifier(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendInput(key);
|
||||||
|
},
|
||||||
|
[sendInput, stickyModifier],
|
||||||
|
);
|
||||||
|
|
||||||
|
const sendLiteralShortcut = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
sendInput(value);
|
||||||
|
setStickyModifier(null);
|
||||||
|
},
|
||||||
|
[sendInput],
|
||||||
|
);
|
||||||
|
|
||||||
if (!isOpen) return null;
|
if (!isOpen) return null;
|
||||||
|
|
||||||
const getStatusIndicator = () => {
|
const getStatusIndicator = () => {
|
||||||
@@ -1035,6 +1120,16 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
|||||||
<Trash2 size={14} />
|
<Trash2 size={14} />
|
||||||
<span className="terminal-action-label">Clear</span>
|
<span className="terminal-action-label">Clear</span>
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
className="terminal-clear-btn"
|
||||||
|
onClick={() => setShowShortcuts((current) => !current)}
|
||||||
|
data-testid="terminal-shortcut-toggle"
|
||||||
|
title="Shortcuts"
|
||||||
|
aria-pressed={showShortcuts}
|
||||||
|
>
|
||||||
|
<Keyboard size={14} />
|
||||||
|
<span className="terminal-action-label">Shortcuts</span>
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="terminal-close"
|
className="terminal-close"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
@@ -1129,6 +1224,60 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{showShortcuts && (
|
||||||
|
<div className="terminal-shortcut-panel" data-testid="terminal-shortcut-panel">
|
||||||
|
<div className="terminal-shortcut-modifier-row">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`terminal-shortcut-btn terminal-shortcut-btn--modifier ${
|
||||||
|
stickyModifier === "ctrl" ? "is-active" : ""
|
||||||
|
}`}
|
||||||
|
data-testid="terminal-modifier-ctrl"
|
||||||
|
onClick={() => toggleModifier("ctrl")}
|
||||||
|
aria-pressed={stickyModifier === "ctrl"}
|
||||||
|
>
|
||||||
|
Ctrl
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`terminal-shortcut-btn terminal-shortcut-btn--modifier ${
|
||||||
|
stickyModifier === "alt" ? "is-active" : ""
|
||||||
|
}`}
|
||||||
|
data-testid="terminal-modifier-alt"
|
||||||
|
onClick={() => toggleModifier("alt")}
|
||||||
|
aria-pressed={stickyModifier === "alt"}
|
||||||
|
>
|
||||||
|
Alt
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="terminal-shortcut-btn"
|
||||||
|
onClick={() => sendLiteralShortcut("\x1b")}
|
||||||
|
>
|
||||||
|
ESC
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="terminal-shortcut-btn"
|
||||||
|
onClick={() => sendLiteralShortcut("\t")}
|
||||||
|
>
|
||||||
|
Tab
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{SHORTCUT_KEYS.map((shortcut) => (
|
||||||
|
<button
|
||||||
|
key={shortcut.label}
|
||||||
|
type="button"
|
||||||
|
className="terminal-shortcut-btn"
|
||||||
|
onClick={() => sendShortcutKey(shortcut.key)}
|
||||||
|
title={shortcut.description}
|
||||||
|
>
|
||||||
|
{shortcut.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Connection status bar */}
|
{/* Connection status bar */}
|
||||||
<div className="terminal-status-bar" data-testid="terminal-status-bar">
|
<div className="terminal-status-bar" data-testid="terminal-status-bar">
|
||||||
<span className={`terminal-connection-status ${connectionStatus}`}>
|
<span className={`terminal-connection-status ${connectionStatus}`}>
|
||||||
@@ -1166,7 +1315,7 @@ export function TerminalModal({ isOpen, onClose, initialCommand, projectId }: Te
|
|||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
<span className="terminal-shortcuts">
|
<span className="terminal-shortcuts">
|
||||||
Ctrl++/- zoom • Ctrl+L clear • Esc close
|
Ctrl++/- zoom • ⌨ Shortcuts panel • Esc close
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||||
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
|
import { render, screen, fireEvent, waitFor, act } from "@testing-library/react";
|
||||||
import { TerminalModal, _resetInitialViewportHeight } from "../TerminalModal";
|
import { TerminalModal, _resetInitialViewportHeight, ctrlChar, altChar } from "../TerminalModal";
|
||||||
import * as useTerminalModule from "../../hooks/useTerminal";
|
import * as useTerminalModule from "../../hooks/useTerminal";
|
||||||
import * as useTerminalSessionsModule from "../../hooks/useTerminalSessions";
|
import * as useTerminalSessionsModule from "../../hooks/useTerminalSessions";
|
||||||
import * as apiModule from "../../api";
|
import * as apiModule from "../../api";
|
||||||
@@ -64,6 +64,18 @@ const mockCreateTerminalSession = vi.mocked(apiModule.createTerminalSession);
|
|||||||
const mockKillPtyTerminalSession = vi.mocked(apiModule.killPtyTerminalSession);
|
const mockKillPtyTerminalSession = vi.mocked(apiModule.killPtyTerminalSession);
|
||||||
const TERMINAL_FONT_SIZE_KEY = "kb-terminal-font-size";
|
const TERMINAL_FONT_SIZE_KEY = "kb-terminal-font-size";
|
||||||
|
|
||||||
|
describe("ctrlChar/altChar helpers", () => {
|
||||||
|
it("maps Ctrl+C/D/Z/L and Alt sequences correctly", () => {
|
||||||
|
expect(ctrlChar("c")).toBe("\x03");
|
||||||
|
expect(ctrlChar("d")).toBe("\x04");
|
||||||
|
expect(ctrlChar("z")).toBe("\x1a");
|
||||||
|
expect(ctrlChar("l")).toBe("\x0c");
|
||||||
|
|
||||||
|
expect(altChar("c")).toBe("\x1bc");
|
||||||
|
expect(altChar("[")).toBe("\x1b[");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Default tab state
|
// Default tab state
|
||||||
const defaultTab = {
|
const defaultTab = {
|
||||||
id: "tab-1",
|
id: "tab-1",
|
||||||
@@ -519,6 +531,104 @@ describe("TerminalModal", () => {
|
|||||||
expect(mockTerminalInstance.open).toHaveBeenCalledWith(terminalDiv);
|
expect(mockTerminalInstance.open).toHaveBeenCalledWith(terminalDiv);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("shortcut panel", () => {
|
||||||
|
it("is hidden by default and toggles from header action", async () => {
|
||||||
|
render(<TerminalModal isOpen={true} onClose={mockOnClose} />);
|
||||||
|
|
||||||
|
expect(screen.queryByTestId("terminal-shortcut-panel")).toBeNull();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId("terminal-shortcut-toggle"));
|
||||||
|
expect(screen.getByTestId("terminal-shortcut-panel")).toBeTruthy();
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId("terminal-shortcut-toggle"));
|
||||||
|
expect(screen.queryByTestId("terminal-shortcut-panel")).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports sticky modifier toggle semantics", async () => {
|
||||||
|
render(<TerminalModal isOpen={true} onClose={mockOnClose} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId("terminal-shortcut-toggle"));
|
||||||
|
|
||||||
|
const ctrlBtn = screen.getByTestId("terminal-modifier-ctrl");
|
||||||
|
const altBtn = screen.getByTestId("terminal-modifier-alt");
|
||||||
|
|
||||||
|
fireEvent.click(ctrlBtn);
|
||||||
|
expect(ctrlBtn.getAttribute("aria-pressed")).toBe("true");
|
||||||
|
|
||||||
|
fireEvent.click(ctrlBtn);
|
||||||
|
expect(ctrlBtn.getAttribute("aria-pressed")).toBe("false");
|
||||||
|
|
||||||
|
fireEvent.click(ctrlBtn);
|
||||||
|
fireEvent.click(altBtn);
|
||||||
|
expect(ctrlBtn.getAttribute("aria-pressed")).toBe("false");
|
||||||
|
expect(altBtn.getAttribute("aria-pressed")).toBe("true");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sends modified and literal keys, then clears sticky modifier", async () => {
|
||||||
|
render(<TerminalModal isOpen={true} onClose={mockOnClose} />);
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByTestId("terminal-shortcut-toggle"));
|
||||||
|
|
||||||
|
const ctrlBtn = screen.getByTestId("terminal-modifier-ctrl");
|
||||||
|
const altBtn = screen.getByTestId("terminal-modifier-alt");
|
||||||
|
|
||||||
|
fireEvent.click(ctrlBtn);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "C" }));
|
||||||
|
expect(mockSendInput).toHaveBeenCalledWith("\x03");
|
||||||
|
expect(ctrlBtn.getAttribute("aria-pressed")).toBe("false");
|
||||||
|
|
||||||
|
fireEvent.click(altBtn);
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "D" }));
|
||||||
|
expect(mockSendInput).toHaveBeenCalledWith("\x1bd");
|
||||||
|
expect(altBtn.getAttribute("aria-pressed")).toBe("false");
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Z" }));
|
||||||
|
expect(mockSendInput).toHaveBeenCalledWith("z");
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "ESC" }));
|
||||||
|
expect(mockSendInput).toHaveBeenCalledWith("\x1b");
|
||||||
|
|
||||||
|
fireEvent.click(screen.getByRole("button", { name: "Tab" }));
|
||||||
|
expect(mockSendInput).toHaveBeenCalledWith("\t");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders shortcut controls on mobile viewport", async () => {
|
||||||
|
const previousInnerWidth = window.innerWidth;
|
||||||
|
const previousOntouchstart = window.ontouchstart;
|
||||||
|
|
||||||
|
Object.defineProperty(window, "innerWidth", {
|
||||||
|
value: 375,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
Object.defineProperty(window, "ontouchstart", {
|
||||||
|
value: null,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
render(<TerminalModal isOpen={true} onClose={mockOnClose} />);
|
||||||
|
fireEvent.click(screen.getByTestId("terminal-shortcut-toggle"));
|
||||||
|
|
||||||
|
expect(screen.getByTestId("terminal-shortcut-panel")).toBeTruthy();
|
||||||
|
expect(screen.getByTestId("terminal-modifier-ctrl")).toBeTruthy();
|
||||||
|
expect(screen.getByTestId("terminal-modifier-alt")).toBeTruthy();
|
||||||
|
} finally {
|
||||||
|
Object.defineProperty(window, "innerWidth", {
|
||||||
|
value: previousInnerWidth,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
Object.defineProperty(window, "ontouchstart", {
|
||||||
|
value: previousOntouchstart,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("font size controls", () => {
|
describe("font size controls", () => {
|
||||||
it("renders controls in the status bar with default font-size value", async () => {
|
it("renders controls in the status bar with default font-size value", async () => {
|
||||||
render(<TerminalModal isOpen={true} onClose={mockOnClose} />);
|
render(<TerminalModal isOpen={true} onClose={mockOnClose} />);
|
||||||
|
|||||||
Reference in New Issue
Block a user