From 846eed72714fcf11ccb273f96fc9bbf35e20c65e Mon Sep 17 00:00:00 2001 From: kenny lopez Date: Fri, 28 Aug 2026 09:20:22 +0100 Subject: [PATCH] Polish welcome channel search picker Signed-off-by: kenny lopez --- .../ui/WelcomeChannelChipPicker.tsx | 115 +++++++++++------- .../ui/WelcomeChannelSettingsCard.tsx | 28 +++-- .../e2e/invites-settings-screenshots.spec.ts | 13 +- 3 files changed, 100 insertions(+), 56 deletions(-) diff --git a/desktop/src/features/community-members/ui/WelcomeChannelChipPicker.tsx b/desktop/src/features/community-members/ui/WelcomeChannelChipPicker.tsx index cf598198fd..3c02dfb167 100644 --- a/desktop/src/features/community-members/ui/WelcomeChannelChipPicker.tsx +++ b/desktop/src/features/community-members/ui/WelcomeChannelChipPicker.tsx @@ -6,11 +6,15 @@ import type { Channel } from "@/shared/api/types"; import { cn } from "@/shared/lib/cn"; import { ACTION_TRAY_SURFACE_CLASS } from "@/shared/ui/actionTray"; import { Button } from "@/shared/ui/button"; +import { + MODAL_SEARCH_INPUT_CLASS, + MODAL_SEARCH_SHELL_CLASS, +} from "@/shared/ui/modalSearchStyles"; import { POPOVER_CUSTOM_ENTER_MOTION_CLASS } from "@/shared/ui/popoverSurface"; type WelcomeChannelChipPickerProps = { channels: Channel[]; - insert: { id: string; url: string }; + insert: { id: string; title: string; url: string }; onClose: () => void; onRemove: () => void; onSelect: (channel: Channel) => void; @@ -25,7 +29,8 @@ export function WelcomeChannelChipPicker({ onSelect, position, }: WelcomeChannelChipPickerProps) { - const [query, setQuery] = React.useState(""); + const inputRef = React.useRef(null); + const [query, setQuery] = React.useState(insert.url ? insert.title : ""); const [highlightedIndex, setHighlightedIndex] = React.useState(0); const matchingChannels = React.useMemo(() => { const normalizedQuery = query.trim().toLowerCase(); @@ -43,6 +48,11 @@ export function WelcomeChannelChipPicker({ .sort((a, b) => a.name.localeCompare(b.name)); }, [channels, query]); const highlightedChannel = matchingChannels[highlightedIndex]; + const isSearching = query.trim().length > 0; + + React.useEffect(() => { + inputRef.current?.focus({ preventScroll: true }); + }, []); function handleKeyDown(event: React.KeyboardEvent) { if (matchingChannels.length === 0) return; @@ -64,7 +74,7 @@ export function WelcomeChannelChipPicker({
-
-
- +
+
-
- {matchingChannels.length > 0 ? ( - matchingChannels.map((channel, index) => ( - - )) - ) : ( -

- No channels found. -

- )} -
+ + {isSearching ? ( +
+ {matchingChannels.length > 0 ? ( + matchingChannels.map((channel, index) => ( + + )) + ) : ( +

+ No channels found. +

+ )} +
+ ) : null}
- onChange({ ...insert, title: event.target.value })} - placeholder={insert.type === "link" ? "Community guide" : "Team photo"} - value={insert.title} - /> -
+
+ + onChange({ ...insert, title: event.target.value }) + } + placeholder={ + insert.type === "link" ? "Community guide" : "Team photo" + } + value={insert.title} + /> +
+
{insert.type === "link" ? ( @@ -370,7 +380,7 @@ function InlineChipEditor({ insert.type === "link" ? "Link destination" : "Image source" } autoCapitalize="off" - className="bg-muted/50 pl-8" + className={cn("h-10 pl-8", CHANNEL_FORM_FIELD_CONTROL_CLASS)} onChange={(event) => onChange({ ...insert, url: event.target.value })} placeholder={ insert.type === "link" diff --git a/desktop/tests/e2e/invites-settings-screenshots.spec.ts b/desktop/tests/e2e/invites-settings-screenshots.spec.ts index 675fa9efbd..826c36a72a 100644 --- a/desktop/tests/e2e/invites-settings-screenshots.spec.ts +++ b/desktop/tests/e2e/invites-settings-screenshots.spec.ts @@ -407,11 +407,18 @@ test("channel inserts search the community channel list", async ({ page }) => { await expect(channelSearch).toBeVisible(); await expect(page.getByLabel("Channel name")).toHaveCount(0); await expect(page.getByLabel("Channel destination")).toHaveCount(0); + await expect(channelEditor.getByRole("listbox")).toHaveCount(0); + + const pickerBeforeSearch = await channelEditor.boundingBox(); await channelSearch.fill("rand"); + const pickerWithResults = await channelEditor.boundingBox(); + expect(pickerWithResults?.height).toBeCloseTo( + pickerBeforeSearch?.height ?? 0, + 0, + ); const randomResult = channelEditor.getByRole("option", { - name: "random", - exact: true, + name: /^random/, }); await expect(randomResult).toBeVisible(); await waitForAnimations(page); @@ -426,7 +433,7 @@ test("channel inserts search the community channel list", async ({ page }) => { await expect( page .getByRole("dialog", { name: "Edit channel" }) - .getByRole("option", { name: "random", exact: true }), + .getByRole("option", { name: /^random/ }), ).toHaveAttribute("aria-selected", "true"); });