feat(FN-788): nest Scripts under Terminal in compact overflow menu

- Restructure Header.tsx to render Scripts as a submenu of Terminal in compact/tablet overflow menus
- Add submenu toggle state, keyboard navigation, and click-outside-to-close behavior
- Add CSS for nested submenu styling with slide-in animation and depth indicators
- Update Header tests to verify Scripts nested under Terminal with toggle interaction
- Update mobile-header-controls and tablet-header-controls tests for new menu hierarchy
- Update README documentation to reflect the Terminal > Scripts submenu structure
This commit is contained in:
gsxdsm
2026-04-03 13:26:02 -07:00
parent ec4cf15223
commit ece4a667f9
7 changed files with 162 additions and 46 deletions

View File

@@ -72,6 +72,13 @@ describe("mobile-header-controls.css", () => {
expect(cssContent).toMatch(/\.mobile-overflow-item:hover/);
});
it("has terminal submenu styles for nested scripts under terminal", () => {
expect(cssContent).toContain(".mobile-overflow-group");
expect(cssContent).toContain(".mobile-overflow-submenu");
expect(cssContent).toContain(".mobile-overflow-subitem");
expect(cssContent).toContain(".mobile-overflow-chevron");
});
it("does not contain obsolete mobile header search wrap rules", () => {
// The old @media (max-width: 640px) and @media (max-width: 480px)
// header search rules should be removed

View File

@@ -172,10 +172,18 @@ describe("tablet header controls", () => {
expect(screen.getByText("Import from GitHub")).toBeDefined();
});
it("overflow menu contains terminal on tablet", () => {
it("overflow menu contains terminal group on tablet", () => {
renderTabletHeader({ onToggleTerminal: noop });
fireEvent.click(screen.getByTitle("More header actions"));
expect(screen.getByTestId("overflow-terminal-group-trigger")).toBeDefined();
});
it("overflow menu contains terminal submenu items when expanded on tablet", () => {
renderTabletHeader({ onToggleTerminal: noop, onOpenScripts: noop });
fireEvent.click(screen.getByTitle("More header actions"));
fireEvent.click(screen.getByTestId("overflow-terminal-group-trigger"));
expect(screen.getByTestId("overflow-terminal-btn")).toBeDefined();
expect(screen.getByTestId("overflow-scripts-btn")).toBeDefined();
});
it("overflow menu contains scheduled tasks on tablet", () => {
@@ -230,10 +238,11 @@ describe("tablet header controls", () => {
expect(onOpenSettings).toHaveBeenCalled();
});
it("calls onToggleTerminal from overflow menu on tablet", () => {
it("calls onToggleTerminal from terminal submenu on tablet", () => {
const onToggleTerminal = vi.fn();
renderTabletHeader({ onToggleTerminal });
fireEvent.click(screen.getByTitle("More header actions"));
fireEvent.click(screen.getByTestId("overflow-terminal-group-trigger"));
fireEvent.click(screen.getByTestId("overflow-terminal-btn"));
expect(onToggleTerminal).toHaveBeenCalled();
});
@@ -278,6 +287,17 @@ describe("tablet header controls", () => {
expect(screen.queryByRole("menu")).toBeNull();
});
it("closes terminal submenu on Escape without closing overflow menu on tablet", () => {
renderTabletHeader({ onToggleTerminal: noop });
fireEvent.click(screen.getByTitle("More header actions"));
fireEvent.click(screen.getByTestId("overflow-terminal-group-trigger"));
expect(screen.getByTestId("overflow-terminal-btn")).toBeDefined();
fireEvent.keyDown(document, { key: "Escape" });
// Submenu closes but overflow menu stays open
expect(screen.queryByTestId("overflow-terminal-btn")).toBeNull();
expect(screen.getByRole("menu")).toBeDefined();
});
// ── Search on tablet ───────────────────────────────────────────
it("renders desktop-style search input on tablet (not mobile search trigger)", () => {