fix(site/src/pages/AgentsPage/components): right-align user chat bubbles (#25000)

This commit is contained in:
Danielle Maywood
2026-05-06 17:44:43 +01:00
committed by GitHub
parent 6a200a49d3
commit 92b1fc48b4
4 changed files with 45 additions and 3 deletions
@@ -235,6 +235,13 @@ const buildStoryArgs = (...messages: TypesGen.ChatMessage[]) => ({
parsedMessages: buildMessages(messages),
});
const LONG_USER_MESSAGE = [
"This is a deliberately long user message that should stay pinned to the",
"right edge while the bubble stops short of filling the entire timeline",
"column. It gives the Storybook test enough content to exercise the",
"maximum width cap.",
].join(" ");
const findAttachmentTile = async (
canvas: ReturnType<typeof within>,
label: string,
@@ -291,6 +298,38 @@ const meta: Meta<typeof ConversationTimeline> = {
export default meta;
type Story = StoryObj<typeof ConversationTimeline>;
/**
* User bubbles should stay right-aligned, shrink to fit short content,
* and cap long content so the timeline keeps some breathing room.
*/
export const UserMessageBubbleAlignment: Story = {
args: buildStoryArgs(
buildUserMessage({
text: LONG_USER_MESSAGE,
}),
),
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const messageText = canvas.getByText(/deliberately long user message/i);
const userRow = messageText.closest('[data-role="user"]');
expect(userRow).not.toBeNull();
const bubble = userRow?.firstElementChild;
expect(bubble).not.toBeNull();
await userEvent.hover(userRow?.parentElement as HTMLElement);
const actions = await canvas.findByTestId("message-actions");
const rowRect = (userRow as HTMLElement).getBoundingClientRect();
const bubbleRect = (bubble as HTMLElement).getBoundingClientRect();
const actionsRect = actions.getBoundingClientRect();
expect(bubbleRect.width).toBeLessThanOrEqual(rowRect.width * 0.81);
expect(Math.abs(rowRect.right - bubbleRect.right)).toBeLessThanOrEqual(2);
expect(Math.abs(rowRect.right - actionsRect.right)).toBeLessThanOrEqual(2);
},
};
/** Regression guard: a single image attachment must not be duplicated. */
export const UserMessageWithSingleImage: Story = {
args: {
@@ -592,7 +592,10 @@ const ChatMessageItem = memo<{
(displayState.hasCopyableContent ||
(isUser && onEditUserMessage)) && (
<div
className="mt-0.5 flex items-center gap-0.5 opacity-0 transition-opacity focus-within:opacity-100 group-hover/msg:opacity-100"
className={cn(
"mt-0.5 flex items-center gap-0.5 opacity-0 transition-opacity focus-within:opacity-100 group-hover/msg:opacity-100",
isUser && "w-full justify-end",
)}
data-testid="message-actions"
>
{displayState.hasCopyableContent && (
@@ -43,7 +43,7 @@ export const UserMessageContent: FC<{
onTextFileClick,
}) => {
return (
<Message className="w-full max-w-none">
<Message className="w-fit max-w-[min(80vw,80%)]">
<MessageContent
className={cn(
"rounded-lg border border-solid border-border-default bg-surface-secondary px-3 py-2 font-sans shadow-sm transition-shadow",
@@ -27,7 +27,7 @@ export const ConversationWithMessages: Story = {
return (
<Conversation>
<ConversationItem {...userItemProps}>
<Message className="my-2 w-full max-w-none">
<Message className="my-2 w-fit max-w-[min(80vw,80%)]">
<MessageContent className="rounded-lg border border-solid border-border-default bg-surface-secondary px-3 py-2 font-sans shadow-sm">
Check why `git fetch` is failing in this workspace.
</MessageContent>