feat(KB-257): add localStorage persistence for QuickEntryBox draft input

- Persist draft task input in localStorage to prevent data loss on refresh
- Restore saved draft when component mounts
- Clear localStorage entry when task is successfully created
- Add comprehensive tests for persistence behavior
- Remove fixed positioning styles from ListView component
This commit is contained in:
gsxdsm
2026-03-30 23:20:04 -07:00
parent b139876e98
commit 42375d2f4c
2 changed files with 118 additions and 2 deletions

View File

@@ -619,4 +619,98 @@ describe("QuickEntryBox", () => {
expect(screen.queryByTestId("subtask-button")).toBeNull();
});
});
describe("localStorage persistence", () => {
beforeEach(() => {
// Clear localStorage before each test
localStorage.clear();
});
afterEach(() => {
localStorage.clear();
});
it("restores description from localStorage on mount", () => {
// Pre-populate localStorage
localStorage.setItem("kb-quick-entry-text", "Saved task description");
renderQuickEntryBox();
const textarea = screen.getByTestId("quick-entry-input");
// Should restore the saved description
expect((textarea as HTMLTextAreaElement).value).toBe("Saved task description");
});
it("updates localStorage when typing", async () => {
renderQuickEntryBox();
const textarea = screen.getByTestId("quick-entry-input");
fireEvent.change(textarea, { target: { value: "Typing this task" } });
// Wait for the useEffect to run
await waitFor(() => {
expect(localStorage.getItem("kb-quick-entry-text")).toBe("Typing this task");
});
});
it("clears localStorage after successful task creation", async () => {
const { props } = renderQuickEntryBox();
const textarea = screen.getByTestId("quick-entry-input");
// Type something to set localStorage
fireEvent.change(textarea, { target: { value: "Task to create" } });
await waitFor(() => {
expect(localStorage.getItem("kb-quick-entry-text")).toBe("Task to create");
});
// Submit the task
fireEvent.keyDown(textarea, { key: "Enter" });
await waitFor(() => {
expect(props.onCreate).toHaveBeenCalled();
});
// localStorage should be cleared
expect(localStorage.getItem("kb-quick-entry-text")).toBeNull();
});
it("clears localStorage when Escape clears non-empty input", async () => {
renderQuickEntryBox();
const textarea = screen.getByTestId("quick-entry-input");
// Type something to set localStorage
fireEvent.focus(textarea);
fireEvent.change(textarea, { target: { value: "Task to clear" } });
await waitFor(() => {
expect(localStorage.getItem("kb-quick-entry-text")).toBe("Task to clear");
});
// Press Escape to clear the input
fireEvent.keyDown(textarea, { key: "Escape" });
// Input and localStorage should be cleared
expect((textarea as HTMLTextAreaElement).value).toBe("");
expect(localStorage.getItem("kb-quick-entry-text")).toBeNull();
});
it("does not clear localStorage on first Escape when closing dropdowns", () => {
renderQuickEntryBox();
const textarea = screen.getByTestId("quick-entry-input");
// Type something and open dropdown
fireEvent.focus(textarea);
fireEvent.change(textarea, { target: { value: "Task with dropdown" } });
fireEvent.click(screen.getByTestId("quick-entry-deps-button"));
// localStorage should have the value
expect(localStorage.getItem("kb-quick-entry-text")).toBe("Task with dropdown");
// First Escape closes dropdown but keeps input
fireEvent.keyDown(textarea, { key: "Escape" });
// Input and localStorage should be preserved
expect((textarea as HTMLTextAreaElement).value).toBe("Task with dropdown");
expect(localStorage.getItem("kb-quick-entry-text")).toBe("Task with dropdown");
});
});
});