Skip to content

Commit ad9aab5

Browse files
committed
feat: improve PostForm focus management
- Refocus textarea after form submission - Add test for Cmd/Ctrl+Enter submission shortcut - Add tests to verify focus behavior
1 parent 4a3db5b commit ad9aab5

2 files changed

Lines changed: 49 additions & 1 deletion

File tree

app/src/components/PostForm.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { ReactElement } from "react";
2-
import { useState } from "react";
2+
import { useState, useRef } from "react";
33
import { useQueryClient } from "@tanstack/react-query";
44

55
import createPost from "../api/createPost";
66

77
export default function PostForm(): ReactElement {
88
const queryClient = useQueryClient();
9+
const textareaRef = useRef<HTMLTextAreaElement>(null);
910
const [form, setForm] = useState({
1011
head: "",
1112
body: "",
@@ -19,6 +20,7 @@ export default function PostForm(): ReactElement {
1920
.then(() => {
2021
setForm({ head: "", body: "" }); // Reset form
2122
queryClient.invalidateQueries({ queryKey: ["posts"] }); // Trigger posts refetch
23+
textareaRef.current?.focus(); // Refocus textarea after submission
2224
})
2325
.catch((error) => {
2426
console.error(error); // eslint-disable-line no-console
@@ -40,6 +42,7 @@ export default function PostForm(): ReactElement {
4042
What's on your mind?
4143
</label>
4244
<textarea
45+
ref={textareaRef}
4346
className="w-full p-3 border rounded-lg shadow-sm focus:ring-2 focus:ring-blue-500 focus:outline-none"
4447
id="head"
4548
name="head"

app/src/components/__tests__/PostForm.test.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,49 @@ describe("PostForm", () => {
201201
// Verify textarea is focused first
202202
expect(document.activeElement).toBe(textarea);
203203
});
204+
205+
it("refocuses textarea after successful form submission", async () => {
206+
const mockCreatePost = vi.mocked(createPost);
207+
mockCreatePost.mockResolvedValueOnce(mockPost);
208+
209+
renderPostForm();
210+
211+
const textarea = screen.getByRole('textbox', { name: /post content/i });
212+
const submitButton = screen.getByRole('button', { name: /send/i });
213+
214+
// Type some text and submit
215+
await userEvent.type(textarea, "Test post content");
216+
await userEvent.click(submitButton);
217+
218+
// Verify textarea is refocused after submission
219+
expect(document.activeElement).toBe(textarea);
220+
});
221+
222+
it("submits form and refocuses textarea on Cmd/Ctrl+Enter", async () => {
223+
const mockCreatePost = vi.mocked(createPost);
224+
mockCreatePost.mockResolvedValueOnce(mockPost);
225+
226+
renderPostForm();
227+
228+
const textarea = screen.getByRole('textbox', { name: /post content/i });
229+
await userEvent.type(textarea, "Test post content");
230+
231+
// Simulate Cmd+Enter (macOS) or Ctrl+Enter (Windows/Linux)
232+
fireEvent.keyDown(textarea, {
233+
key: "Enter",
234+
code: "Enter",
235+
metaKey: true, // Cmd key on macOS
236+
});
237+
238+
// Verify form was submitted
239+
await waitFor(() => {
240+
expect(mockCreatePost).toHaveBeenCalledWith({
241+
head: "Test post content",
242+
body: "",
243+
});
244+
});
245+
246+
// Verify textarea is refocused
247+
expect(document.activeElement).toBe(textarea);
248+
});
204249
});

0 commit comments

Comments
 (0)