mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Fix mention autocomplete layout in narrow threads (#1282)
Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
This commit is contained in:
@@ -96,44 +96,58 @@ export const MentionAutocomplete = React.memo(function MentionAutocomplete({
|
||||
avatarUrl={suggestion.avatarUrl ?? null}
|
||||
displayName={suggestion.displayName}
|
||||
size="xs"
|
||||
testId="mention-suggestion-avatar"
|
||||
/>
|
||||
<span className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<span className="flex min-w-0 flex-1 items-baseline gap-1">
|
||||
<span className="truncate font-medium">
|
||||
{suggestion.displayName}
|
||||
</span>
|
||||
{suggestion.isAgent ? (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex shrink-0 items-center gap-1 text-xs",
|
||||
index === selectedIndex
|
||||
? "text-accent-foreground/70"
|
||||
: "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
<Bot
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4"
|
||||
data-testid="mention-agent-icon"
|
||||
/>
|
||||
{agentLabel}
|
||||
</span>
|
||||
) : suggestion.role ? (
|
||||
<Badge variant="secondary">{suggestion.role}</Badge>
|
||||
) : null}
|
||||
<span className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<span
|
||||
className="min-w-0 break-words font-medium leading-snug"
|
||||
title={suggestion.displayName}
|
||||
>
|
||||
{suggestion.displayName}
|
||||
</span>
|
||||
{suggestion.ownerLabel || suggestion.notInChannel ? (
|
||||
{suggestion.isAgent ||
|
||||
suggestion.role ||
|
||||
suggestion.ownerLabel ||
|
||||
suggestion.notInChannel ? (
|
||||
<span
|
||||
className={cn(
|
||||
"ml-auto min-w-0 shrink truncate text-xs",
|
||||
"flex min-w-0 items-center gap-1.5 text-2xs leading-none",
|
||||
index === selectedIndex
|
||||
? "text-accent-foreground/65"
|
||||
? "text-accent-foreground/60"
|
||||
: "text-muted-foreground",
|
||||
)}
|
||||
>
|
||||
{suggestion.ownerLabel
|
||||
? `owned by ${suggestion.ownerLabel}${suggestion.notInChannel ? " · not in channel" : ""}`
|
||||
: "not in channel"}
|
||||
{suggestion.isAgent ? (
|
||||
<span className="inline-flex shrink-0 items-center gap-1">
|
||||
<Bot
|
||||
aria-hidden="true"
|
||||
className="h-3.5 w-3.5"
|
||||
data-testid="mention-agent-icon"
|
||||
/>
|
||||
{agentLabel}
|
||||
</span>
|
||||
) : suggestion.role ? (
|
||||
<Badge
|
||||
className="max-w-24 shrink-0 truncate"
|
||||
variant="secondary"
|
||||
>
|
||||
{suggestion.role}
|
||||
</Badge>
|
||||
) : null}
|
||||
{suggestion.ownerLabel || suggestion.notInChannel ? (
|
||||
<span
|
||||
className="min-w-0 truncate"
|
||||
title={
|
||||
suggestion.ownerLabel
|
||||
? `owned by ${suggestion.ownerLabel}${suggestion.notInChannel ? " · not in channel" : ""}`
|
||||
: "not in channel"
|
||||
}
|
||||
>
|
||||
{suggestion.ownerLabel
|
||||
? `owned by ${suggestion.ownerLabel}${suggestion.notInChannel ? " · not in channel" : ""}`
|
||||
: "not in channel"}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
|
||||
@@ -46,6 +46,46 @@ function commandCount(commands: string[], command: string) {
|
||||
return commands.filter((entry) => entry === command).length;
|
||||
}
|
||||
|
||||
async function emitMockMessage(
|
||||
page: import("@playwright/test").Page,
|
||||
channelName: string,
|
||||
content: string,
|
||||
options?: {
|
||||
parentEventId?: string;
|
||||
pubkey?: string;
|
||||
},
|
||||
) {
|
||||
const event = await page.evaluate(
|
||||
({ ch, msg, parentEventId, pubkey }) => {
|
||||
return (
|
||||
window as Window & {
|
||||
__BUZZ_E2E_EMIT_MOCK_MESSAGE__?: (input: {
|
||||
channelName: string;
|
||||
content: string;
|
||||
parentEventId?: string | null;
|
||||
pubkey?: string;
|
||||
}) => { id: string; created_at: number; pubkey: string };
|
||||
}
|
||||
).__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({
|
||||
channelName: ch,
|
||||
content: msg,
|
||||
parentEventId: parentEventId ?? undefined,
|
||||
pubkey: pubkey ?? undefined,
|
||||
});
|
||||
},
|
||||
{
|
||||
ch: channelName,
|
||||
msg: content,
|
||||
parentEventId: options?.parentEventId ?? null,
|
||||
pubkey: options?.pubkey ?? TEST_IDENTITIES.alice.pubkey,
|
||||
},
|
||||
);
|
||||
if (!event) {
|
||||
throw new Error("Mock message emitter is not installed");
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
async function waitForMockLiveSubscription(
|
||||
page: import("@playwright/test").Page,
|
||||
channelName: string,
|
||||
@@ -135,6 +175,78 @@ test("@ trigger shows unified autocomplete with agents first", async ({
|
||||
expect(bobIndex).toBeLessThan(charlieIndex);
|
||||
});
|
||||
|
||||
test("thread autocomplete keeps multiple long names readable in a narrow panel", async ({
|
||||
page,
|
||||
}) => {
|
||||
await installMockBridge(page, {
|
||||
managedAgents: [
|
||||
{
|
||||
pubkey:
|
||||
"9999999999999999999999999999999999999999999999999999999999999999",
|
||||
name: "Brain With A Very Long Name",
|
||||
status: "stopped",
|
||||
},
|
||||
{
|
||||
pubkey:
|
||||
"9999999999999999999999999999999999999999999999999999999999999998",
|
||||
name: "Brainstorming Assistant With A Long Name",
|
||||
status: "stopped",
|
||||
},
|
||||
{
|
||||
pubkey:
|
||||
"9999999999999999999999999999999999999999999999999999999999999997",
|
||||
name: "Brainy Helper With Another Long Name",
|
||||
status: "stopped",
|
||||
},
|
||||
],
|
||||
});
|
||||
await page.setViewportSize({ width: 900, height: 640 });
|
||||
await page.addInitScript(() => {
|
||||
window.sessionStorage.setItem("buzz.desktop.thread-panel-width", "300");
|
||||
});
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
await expect(page.getByTestId("chat-title")).toHaveText("general");
|
||||
await page.setViewportSize({ width: 760, height: 640 });
|
||||
|
||||
await emitMockMessage(page, "general", "Reply to open the thread", {
|
||||
parentEventId: "mock-general-welcome",
|
||||
});
|
||||
const threadSummary = page.getByTestId("message-thread-summary").first();
|
||||
await expect(threadSummary).toBeVisible();
|
||||
await threadSummary.click();
|
||||
|
||||
const threadPanel = page.getByTestId("message-thread-panel");
|
||||
await expect(threadPanel).toBeVisible();
|
||||
const panelBox = await threadPanel.boundingBox();
|
||||
expect(panelBox?.width ?? Number.POSITIVE_INFINITY).toBeLessThanOrEqual(320);
|
||||
|
||||
const input = threadPanel.getByTestId("message-input");
|
||||
await input.fill("@Brain");
|
||||
|
||||
const dropdown = threadPanel.getByTestId("mention-autocomplete");
|
||||
await expect(dropdown).toBeVisible();
|
||||
|
||||
for (const name of [
|
||||
"Brain With A Very Long Name",
|
||||
"Brainstorming Assistant With A Long Name",
|
||||
"Brainy Helper With Another Long Name",
|
||||
]) {
|
||||
const row = dropdown.locator("button", { hasText: name });
|
||||
await expect(row).toBeVisible();
|
||||
await expect(
|
||||
row.getByTestId("mention-suggestion-avatar-fallback"),
|
||||
).toBeVisible();
|
||||
await expect(row.getByText("agent")).toBeVisible();
|
||||
await expect(row.getByText(/owned by npub1mock/)).toBeVisible();
|
||||
|
||||
await expect(row.getByText(name)).not.toHaveCSS(
|
||||
"text-overflow",
|
||||
"ellipsis",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("autocomplete filters suggestions as user types", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await page.getByTestId("channel-general").click();
|
||||
|
||||
Reference in New Issue
Block a user