diff --git a/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx b/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx index a10fac83c0..e107d64b01 100644 --- a/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx +++ b/packages/dashboard/app/__tests__/quick-entry-expanded-height.test.tsx @@ -93,7 +93,7 @@ function renderQuickEntryBox(props = {}) { return render(); } -function expandQuickEntry() { +function toggleQuickEntry() { const toggleButton = screen.getByTestId("quick-entry-toggle"); fireEvent.click(toggleButton); } @@ -147,25 +147,24 @@ describe("quick-entry-expanded-height CSS contract (FN-1631)", () => { * (`.quick-entry-box--expanded .quick-entry-input`) inadvertently override the * expanded-height rules set by `.quick-entry-input--expanded`. */ - it("expanded class applies when quick-entry is expanded via toggle", () => { + it("expanded class applies by default and toggle collapses quick-entry", () => { mockDesktopViewport(); renderQuickEntryBox(); const textarea = screen.getByTestId("quick-entry-input"); const box = screen.getByTestId("quick-entry-box"); - // Initially collapsed - expect(box.classList.contains("quick-entry-box--collapsed")).toBe(true); - expect(box.classList.contains("quick-entry-box--expanded")).toBe(false); - expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); - - // Expand via toggle - expandQuickEntry(); - - // Verify expanded state + // Initially expanded expect(box.classList.contains("quick-entry-box--expanded")).toBe(true); expect(box.classList.contains("quick-entry-box--collapsed")).toBe(false); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); + + // Collapse via toggle + toggleQuickEntry(); + + expect(box.classList.contains("quick-entry-box--collapsed")).toBe(true); + expect(box.classList.contains("quick-entry-box--expanded")).toBe(false); + expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); }); /** @@ -251,7 +250,8 @@ describe("quick-entry-expanded-height CSS contract (FN-1631)", () => { const textarea = screen.getByTestId("quick-entry-input"); const box = screen.getByTestId("quick-entry-box"); - // Collapsed state + // Collapse from the expanded default + toggleQuickEntry(); expect(box.classList.contains("quick-entry-box--collapsed")).toBe(true); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); diff --git a/packages/dashboard/app/components/QuickEntryBox.tsx b/packages/dashboard/app/components/QuickEntryBox.tsx index 6851bdc69d..74838a0f03 100644 --- a/packages/dashboard/app/components/QuickEntryBox.tsx +++ b/packages/dashboard/app/components/QuickEntryBox.tsx @@ -98,10 +98,10 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels, }); const [isSubmitting, setIsSubmitting] = useState(false); // isExpanded controls textarea height styling (auto-resize) - const [isExpanded, setIsExpanded] = useState(false); + const [isExpanded, setIsExpanded] = useState(true); // isDisclosureExpanded controls visibility of the controls panel (Deps, Models, etc.) - // Always starts collapsed — user must explicitly toggle each session - const [isDisclosureExpanded, setIsDisclosureExpanded] = useState(false); + // Starts expanded by default — controls visible immediately + const [isDisclosureExpanded, setIsDisclosureExpanded] = useState(true); const textareaRef = useRef(null); const fileInputRef = useRef(null); const justResetRef = useRef(false); diff --git a/packages/dashboard/app/components/__tests__/App.test.tsx b/packages/dashboard/app/components/__tests__/App.test.tsx index fba629552b..c21fa5359f 100644 --- a/packages/dashboard/app/components/__tests__/App.test.tsx +++ b/packages/dashboard/app/components/__tests__/App.test.tsx @@ -3610,8 +3610,8 @@ describe("App onboarding reopen", () => { // Onboarding should NOT be open initially expect(screen.queryByText("Set Up AI")).toBeNull(); - // Open Settings via header - const settingsBtn = screen.getByRole("button", { name: /settings/i }); + // Open Settings via header (quick-entry controls also mention Settings in labels) + const settingsBtn = screen.getByTitle("Settings"); fireEvent.click(settingsBtn); await waitFor(() => { @@ -3667,8 +3667,8 @@ describe("App onboarding reopen", () => { await waitForAppShell(); - // Open Settings via header - const settingsBtn = screen.getByRole("button", { name: /settings/i }); + // Open Settings via header (quick-entry controls also mention Settings in labels) + const settingsBtn = screen.getByTitle("Settings"); fireEvent.click(settingsBtn); await waitFor(() => { diff --git a/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx b/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx index b4029902a0..43ec7de025 100644 --- a/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx +++ b/packages/dashboard/app/components/__tests__/QuickEntryBox.test.tsx @@ -186,7 +186,7 @@ vi.mock("../CustomModelDropdown", () => ({ })); function renderQuickEntryBox(props = {}, { startExpanded = false } = {}) { - // Component defaults to collapsed; set localStorage to expand if needed + // Legacy option retained for older test call sites; disclosure now defaults expanded. if (startExpanded) { localStorage.setItem("kb-quick-entry-expanded", "true"); } @@ -201,10 +201,16 @@ function renderQuickEntryBox(props = {}, { startExpanded = false } = {}) { return { ...result, props: { ...defaultProps, ...props } }; } -// Helper to expand the QuickEntryBox by clicking the toggle button +// Helper to ensure the QuickEntryBox is expanded by clicking the toggle only when needed. function expandQuickEntry() { const toggleButton = screen.getByTestId("quick-entry-toggle"); - fireEvent.click(toggleButton); + if (toggleButton.getAttribute("aria-expanded") !== "true") { + fireEvent.click(toggleButton); + } +} + +function toggleQuickEntry() { + fireEvent.click(screen.getByTestId("quick-entry-toggle")); } function openDepsMenu() { @@ -335,49 +341,47 @@ describe("QuickEntryBox", () => { expect(inputRect.width).toBeGreaterThanOrEqual(containerRect.width * 0.8); }); - it("does NOT expand on focus when autoExpand is false", () => { + it("starts expanded even when autoExpand is false", () => { renderQuickEntryBox({ autoExpand: false }); const textarea = screen.getByTestId("quick-entry-input"); fireEvent.focus(textarea); - // Should NOT auto-expand on focus when autoExpand is false - expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); + expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); }); - it("expands on focus by default (backward compatible)", () => { + it("starts expanded by default (backward compatible)", () => { renderQuickEntryBox(); const textarea = screen.getByTestId("quick-entry-input"); fireEvent.focus(textarea); - // Should auto-expand on focus by default (autoExpand defaults to true) expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); }); - it("toggle button expands the view", () => { + it("toggle button starts expanded and collapses the view", () => { renderQuickEntryBox({}); const textarea = screen.getByTestId("quick-entry-input"); const toggleButton = screen.getByTestId("quick-entry-toggle"); const controls = document.getElementById("quick-entry-controls"); - // Initially not expanded - expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); - expect(screen.getByTestId("quick-entry-box").classList.contains("quick-entry-box--collapsed")).toBe(true); - expect(screen.getByTestId("quick-entry-box").className).toContain("quick-entry-box"); - expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); - expect(textarea.getAttribute("aria-expanded")).toBe("false"); - expect(controls?.hasAttribute("hidden")).toBe(true); - - // Click toggle to expand - expandQuickEntry(); - - // Now expanded + // Initially expanded expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); expect(screen.getByTestId("quick-entry-box").classList.contains("quick-entry-box--expanded")).toBe(true); + expect(screen.getByTestId("quick-entry-box").className).toContain("quick-entry-box"); expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); expect(textarea.getAttribute("aria-expanded")).toBe("true"); expect(controls?.hasAttribute("hidden")).toBe(false); + + // Click toggle to collapse + toggleQuickEntry(); + + // Now collapsed + expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); + expect(screen.getByTestId("quick-entry-box").classList.contains("quick-entry-box--collapsed")).toBe(true); + expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); + expect(textarea.getAttribute("aria-expanded")).toBe("false"); + expect(controls?.hasAttribute("hidden")).toBe(true); }); it("toggle button collapses the view when expanded", () => { @@ -385,12 +389,11 @@ describe("QuickEntryBox", () => { const textarea = screen.getByTestId("quick-entry-input"); const box = screen.getByTestId("quick-entry-box"); - // Expand first - expandQuickEntry(); + // Starts expanded expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); - // Click toggle again to collapse - expandQuickEntry(); + // Click toggle to collapse + toggleQuickEntry(); // Now collapsed expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); @@ -402,15 +405,15 @@ describe("QuickEntryBox", () => { renderQuickEntryBox({}); const box = screen.getByTestId("quick-entry-box"); - expect(box.classList.contains("quick-entry-box--collapsed")).toBe(true); - expect(box.classList.contains("quick-entry-box--expanded")).toBe(false); - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); - - expandQuickEntry(); - expect(box.classList.contains("quick-entry-box--expanded")).toBe(true); expect(box.classList.contains("quick-entry-box--collapsed")).toBe(false); expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); + + toggleQuickEntry(); + + expect(box.classList.contains("quick-entry-box--collapsed")).toBe(true); + expect(box.classList.contains("quick-entry-box--expanded")).toBe(false); + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); }); it("does NOT collapse on blur when empty", async () => { @@ -472,6 +475,7 @@ describe("QuickEntryBox", () => { const { props } = renderQuickEntryBox({}); const textarea = screen.getByTestId("quick-entry-input"); + toggleQuickEntry(); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); fireEvent.change(textarea, { target: { value: "Line 1" } }); @@ -689,10 +693,10 @@ describe("QuickEntryBox", () => { it("shows inline deps/models/save controls when expanded", () => { renderQuickEntryBox({}); - // Initially, controls region is collapsed/hidden - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + // Controls region starts expanded/visible + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); - // Expand and type something + // Type something expandQuickEntry(); const textarea = screen.getByTestId("quick-entry-input"); fireEvent.change(textarea, { target: { value: "Task with deps" } }); @@ -705,10 +709,10 @@ describe("QuickEntryBox", () => { it("shows deps/models/save controls directly when expanded", () => { renderQuickEntryBox({}); - // Initially, controls region is collapsed/hidden - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + // Controls region starts expanded/visible + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); - // Expand and type something + // Type something expandQuickEntry(); const textarea = screen.getByTestId("quick-entry-input"); fireEvent.change(textarea, { target: { value: "Task with models" } }); @@ -721,10 +725,10 @@ describe("QuickEntryBox", () => { it("shows Plan and Subtask buttons when expanded", () => { renderQuickEntryBox({}); - // Initially, controls region is collapsed/hidden - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + // Controls region starts expanded/visible + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); - // Expand and type something + // Type something expandQuickEntry(); const textarea = screen.getByTestId("quick-entry-input"); fireEvent.change(textarea, { target: { value: "Task to plan" } }); @@ -1806,38 +1810,31 @@ describe("QuickEntryBox", () => { }); describe("State sync between isExpanded and isDisclosureExpanded", () => { - it("focus then toggle shows controls without collapsing textarea", () => { + it("focus leaves initially visible controls and expanded textarea intact", () => { renderQuickEntryBox(); const textarea = screen.getByTestId("quick-entry-input"); const controls = document.getElementById("quick-entry-controls"); - // Focus auto-expands textarea height (isExpanded=true) but NOT disclosure fireEvent.focus(textarea); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); - expect(controls?.hasAttribute("hidden")).toBe(true); // controls still hidden + expect(controls?.hasAttribute("hidden")).toBe(false); - // Now click toggle — should expand disclosure AND keep textarea expanded expandQuickEntry(); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); expect(controls?.hasAttribute("hidden")).toBe(false); }); - it("toggle twice from focused state returns to collapsed", () => { + it("toggle from focused expanded state returns to collapsed", () => { renderQuickEntryBox(); const textarea = screen.getByTestId("quick-entry-input"); const controls = document.getElementById("quick-entry-controls"); - // Focus → auto-expand textarea fireEvent.focus(textarea); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); - - // Toggle expand - expandQuickEntry(); - expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); expect(controls?.hasAttribute("hidden")).toBe(false); // Toggle collapse — both states should collapse together - expandQuickEntry(); + toggleQuickEntry(); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); expect(controls?.hasAttribute("hidden")).toBe(true); }); @@ -1867,15 +1864,13 @@ describe("QuickEntryBox", () => { renderQuickEntryBox(); const textarea = screen.getByTestId("quick-entry-input"); - // Focus auto-expands textarea height but NOT disclosure fireEvent.focus(textarea); expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true); - // aria-expanded should still be false (reflects disclosure, not height) - expect(textarea.getAttribute("aria-expanded")).toBe("false"); - - // Toggle expand — now aria-expanded should be true - expandQuickEntry(); expect(textarea.getAttribute("aria-expanded")).toBe("true"); + + toggleQuickEntry(); + expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false); + expect(textarea.getAttribute("aria-expanded")).toBe("false"); }); }); @@ -1896,10 +1891,10 @@ describe("QuickEntryBox", () => { renderQuickEntryBox(); const toggleButton = screen.getByTestId("quick-entry-toggle"); - // Should NOT restore the saved disclosure state — always starts collapsed - expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); - // Controls should be hidden - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + // Should ignore the saved disclosure state and use the expanded default + expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); + // Controls should be visible + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); }); it("saved draft description does not reveal controls panel", () => { @@ -1912,9 +1907,9 @@ describe("QuickEntryBox", () => { // Description should be restored expect((textarea as HTMLTextAreaElement).value).toBe("Previously saved draft task"); - // But controls should remain hidden — draft text does not auto-expand disclosure - expect(controls?.hasAttribute("hidden")).toBe(true); - expect(screen.getByTestId("quick-entry-toggle").getAttribute("aria-expanded")).toBe("false"); + // Controls are visible by default; draft text does not override disclosure state + expect(controls?.hasAttribute("hidden")).toBe(false); + expect(screen.getByTestId("quick-entry-toggle").getAttribute("aria-expanded")).toBe("true"); }); it("cleans up legacy kb-quick-entry-expanded key on mount", async () => { @@ -1929,36 +1924,34 @@ describe("QuickEntryBox", () => { }); }); - it("defaults to collapsed when localStorage is empty", () => { + it("defaults to expanded when localStorage is empty", () => { renderQuickEntryBox(); const toggleButton = screen.getByTestId("quick-entry-toggle"); - // Should default to collapsed (false) — controls hidden until user expands - expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); - // Controls should be hidden - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); }); it("does not persist disclosure state to localStorage when toggling", async () => { renderQuickEntryBox({}); const toggleButton = screen.getByTestId("quick-entry-toggle"); - // Initially collapsed - expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); - - // Click to expand - fireEvent.click(toggleButton); - - // Should be expanded + // Initially expanded expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); - // localStorage should NOT be updated — disclosure is ephemeral - expect(localStorage.getItem("kb-quick-entry-expanded")).toBeNull(); // Click to collapse fireEvent.click(toggleButton); // Should be collapsed expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); + // localStorage should NOT be updated — disclosure is ephemeral + expect(localStorage.getItem("kb-quick-entry-expanded")).toBeNull(); + + // Click to expand + fireEvent.click(toggleButton); + + // Should be expanded + expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); // localStorage still should not have the key expect(localStorage.getItem("kb-quick-entry-expanded")).toBeNull(); }); @@ -1967,16 +1960,16 @@ describe("QuickEntryBox", () => { renderQuickEntryBox({}); const toggleButton = screen.getByTestId("quick-entry-toggle"); - // Initially collapsed - expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); - - // Click to expand - fireEvent.click(toggleButton); + // Initially expanded expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); // Click to collapse fireEvent.click(toggleButton); expect(toggleButton.getAttribute("aria-expanded")).toBe("false"); + + // Click to expand + fireEvent.click(toggleButton); + expect(toggleButton.getAttribute("aria-expanded")).toBe("true"); }); it("restores description from localStorage on mount", () => { @@ -2067,10 +2060,10 @@ describe("QuickEntryBox", () => { it("shows refine button when expanded and text is entered", () => { renderQuickEntryBox({}); - // Initially, controls region is collapsed/hidden - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + // Controls region starts expanded/visible + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); - // Expand and type something + // Type something expandQuickEntry(); const textarea = screen.getByTestId("quick-entry-input"); fireEvent.change(textarea, { target: { value: "Task to refine" } }); @@ -2382,10 +2375,10 @@ describe("QuickEntryBox", () => { }); describe("Button visibility when collapsed", () => { - it("controls div has hidden attribute when not expanded", () => { + it("controls div does not have hidden attribute by default", () => { renderQuickEntryBox({}); const controls = document.getElementById("quick-entry-controls"); - expect(controls?.hasAttribute("hidden")).toBe(true); + expect(controls?.hasAttribute("hidden")).toBe(false); }); it("toggle button is always visible regardless of expanded state", () => { @@ -2393,10 +2386,10 @@ describe("QuickEntryBox", () => { expect(screen.getByTestId("quick-entry-toggle")).toBeTruthy(); }); - it("shows inline controls after expand", () => { + it("shows inline controls by default", () => { renderQuickEntryBox({}); - expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); + expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); expandQuickEntry(); @@ -2415,7 +2408,7 @@ describe("QuickEntryBox", () => { expandQuickEntry(); expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(false); - expandQuickEntry(); + toggleQuickEntry(); expect(document.getElementById("quick-entry-controls")?.hasAttribute("hidden")).toBe(true); }); @@ -2436,6 +2429,7 @@ describe("QuickEntryBox", () => { it("does not render actions when not expanded", () => { renderQuickEntryBox({}); + toggleQuickEntry(); expect(screen.queryByTestId("quick-entry-actions")).toBeNull(); });