From e3258fda187d1e1339470562a5c05aaaad4a50fa Mon Sep 17 00:00:00 2001 From: eti Date: Fri, 25 Sep 2026 11:59:42 +0200 Subject: [PATCH] web/comment: keep the reply field open under every thread Signed-off-by: eti --- .../lib/components/comment/CommentBox.svelte | 19 ++--- .../comment/CommentCard.stories.svelte | 29 ++----- .../lib/components/comment/CommentCard.svelte | 34 ++------ .../components/comment/CommentEditor.svelte | 85 ++++++++++++------- 4 files changed, 78 insertions(+), 89 deletions(-) diff --git a/web/src/lib/components/comment/CommentBox.svelte b/web/src/lib/components/comment/CommentBox.svelte index 77038e0d5..83100e5da 100644 --- a/web/src/lib/components/comment/CommentBox.svelte +++ b/web/src/lib/components/comment/CommentBox.svelte @@ -16,9 +16,7 @@ markup: MarkupContext; // "thread" tucks the box into a comment card, matching its canvas background variant?: "default" | "thread"; - autofocus?: boolean; onsubmitted?: (submitted: ThreadInput) => void; - oncancel?: () => void; } let { @@ -30,9 +28,7 @@ authorHandle, markup, variant = "default", - autofocus = false, - onsubmitted, - oncancel + onsubmitted }: Props = $props(); const isThread = $derived(variant === "thread"); @@ -47,24 +43,25 @@ {authorDid} {authorHandle} {markup} - rows={isThread ? 4 : 6} placeholder={isThread - ? "Write a reply. Markdown is supported." + ? "Leave a reply..." : "Add to the discussion. Markdown is supported."} submitLabel={isThread ? "Reply" : "Comment"} submitIcon={isThread ? Reply : MessageSquarePlus} - {autofocus} + compact={isThread} + inset={!isThread} {onsubmitted} - {oncancel} /> {/snippet} {#if isThread} -
+
{@render editor()}
{:else} - + {@render editor()} {/if} diff --git a/web/src/lib/components/comment/CommentCard.stories.svelte b/web/src/lib/components/comment/CommentCard.stories.svelte index 8bd91aab2..962cae66b 100644 --- a/web/src/lib/components/comment/CommentCard.stories.svelte +++ b/web/src/lib/components/comment/CommentCard.stories.svelte @@ -31,18 +31,20 @@ - + { const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("button", { name: "Leave a reply..." })); - const textarea = await waitFor(() => canvas.getByPlaceholderText(/write a reply/i)); + const textarea = canvas.getByPlaceholderText("Leave a reply..."); + expect(canvas.queryByRole("button", { name: "Reply" })).toBeNull(); + expect(canvas.queryByRole("switch")).toBeNull(); + await userEvent.type(textarea, "first line{Shift>}{Enter}{/Shift}second line"); await expect(canvas.getByRole("button", { name: "Reply" })).toBeInTheDocument(); - await expect(canvas.getByRole("button", { name: "Cancel" })).toBeInTheDocument(); - // the editor should be focused so the user can type immediately - await waitFor(() => expect(textarea).toHaveFocus()); + expect(canvas.queryByRole("button", { name: "Cancel" })).toBeNull(); + await userEvent.clear(textarea); + await waitFor(() => expect(canvas.queryByRole("button", { name: "Reply" })).toBeNull()); }} /> @@ -61,21 +63,6 @@ }} /> - { - const canvas = within(canvasElement); - await userEvent.click(canvas.getByRole("button", { name: "Leave a reply..." })); - await waitFor(() => canvas.getByPlaceholderText(/write a reply/i)); - await userEvent.click(canvas.getByRole("button", { name: /comment as alice/i })); - await userEvent.click(await canvas.findByRole("menuitem", { name: /bob/i })); - await waitFor(() => - expect(canvas.getByRole("button", { name: /comment as bob/i })).toBeVisible() - ); - }} -/> - (null); let reactionsByUri = $derived.by(() => { @@ -115,7 +114,7 @@ {#snippet commentActions(comment: CommentView, noun: "comment" | "reply")}
-
+ +
- {:else if replying && currentUser} + {:else if currentUser}
{ - replying = false; - onsubmitted?.(submitted); - }} - oncancel={() => (replying = false)} + {onsubmitted} />
{:else}
- {#if currentUser} - - {:else} - - Login to leave a reply - - {/if} + + Login to leave a reply +
{/if} diff --git a/web/src/lib/components/comment/CommentEditor.svelte b/web/src/lib/components/comment/CommentEditor.svelte index 07a81e24e..4dcfcb09f 100644 --- a/web/src/lib/components/comment/CommentEditor.svelte +++ b/web/src/lib/components/comment/CommentEditor.svelte @@ -14,6 +14,7 @@ import Button from "$lib/components/ui/Button.svelte"; import ErrorAlert from "$lib/components/ui/Error.svelte"; import MarkdownEditor from "$lib/components/ui/MarkdownEditor.svelte"; + import Separator from "$lib/components/ui/Separator.svelte"; import Spinner from "$lib/components/ui/Spinner.svelte"; import { renderMarkup, type MarkupContext } from "$lib/markup"; import { createAction } from "$lib/action.svelte"; @@ -40,6 +41,8 @@ submitLabel: string; submitIcon: Component; autofocus?: boolean; + compact?: boolean; + inset?: boolean; onsubmitted?: (submitted: ThreadInput) => void; oncancel?: () => void; } @@ -61,6 +64,8 @@ submitLabel, submitIcon, autofocus = false, + compact = false, + inset = false, onsubmitted, oncancel }: Props = $props(); @@ -136,7 +141,19 @@ const canSubmit = $derived(body.trim() !== "" && !publish.loading); -
+{#snippet submit()} + +{/snippet} + + - + {#if inset} + + {/if} + +
+ -
- {#if !rkey} - acting.did, (did) => (picked = did)} - label="Comment as" - disabled={publish.loading} - group="comment-editor" - /> + {#if !compact} +
+ {#if !rkey} + acting.did, (did) => (picked = did)} + label="Comment as" + disabled={publish.loading} + group="comment-editor" + /> + {/if} +
+ {#if oncancel} + + {/if} + {@render submit()} +
+
{/if} -
- {#if oncancel} - - {/if} - -
-- 2.51.2