fix(FN-725): sync isExpanded and isDisclosureExpanded states in QuickEntryBox
- Fix state desync between isExpanded and isDisclosureExpanded in QuickEntryBox component - Ensure both states update together to prevent UI inconsistencies - Add regression tests for state synchronization scenarios - Add 72 lines of new test coverage in QuickEntryBox.test.tsx
This commit is contained in:
@@ -473,8 +473,11 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
const showExpandedControls = isDisclosureExpanded;
|
||||
|
||||
const toggleExpanded = useCallback(() => {
|
||||
setIsDisclosureExpanded((prev) => !prev);
|
||||
setIsExpanded((prev) => !prev);
|
||||
setIsDisclosureExpanded((prev) => {
|
||||
const next = !prev;
|
||||
setIsExpanded(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -493,7 +496,7 @@ export function QuickEntryBox({ onCreate, addToast, tasks = [], availableModels,
|
||||
data-testid="quick-entry-input"
|
||||
rows={1}
|
||||
aria-controls="quick-entry-controls"
|
||||
aria-expanded={isExpanded}
|
||||
aria-expanded={isDisclosureExpanded}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -827,6 +827,78 @@ describe("QuickEntryBox", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("State sync between isExpanded and isDisclosureExpanded", () => {
|
||||
it("focus then toggle shows controls without collapsing textarea", () => {
|
||||
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
|
||||
|
||||
// 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", () => {
|
||||
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();
|
||||
expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(false);
|
||||
expect(controls?.hasAttribute("hidden")).toBe(true);
|
||||
});
|
||||
|
||||
it("after task creation with autoExpand, focus restore does not show controls unless toggle was used", async () => {
|
||||
const { props } = renderQuickEntryBox();
|
||||
const textarea = screen.getByTestId("quick-entry-input");
|
||||
const controls = document.getElementById("quick-entry-controls");
|
||||
|
||||
// Type and submit without toggling disclosure
|
||||
fireEvent.change(textarea, { target: { value: "New task" } });
|
||||
fireEvent.keyDown(textarea, { key: "Enter" });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(props.onCreate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// After creation, focus is restored → autoExpand triggers isExpanded=true
|
||||
// But disclosure should still be false (controls hidden)
|
||||
expect(textarea.classList.contains("quick-entry-input--expanded")).toBe(true);
|
||||
expect(controls?.hasAttribute("hidden")).toBe(true);
|
||||
});
|
||||
|
||||
it("textarea aria-expanded reflects disclosure state, not textarea height", () => {
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
describe("localStorage persistence", () => {
|
||||
beforeEach(() => {
|
||||
// Clear localStorage before each test
|
||||
|
||||
Reference in New Issue
Block a user