From 30aa3740babaa442dd4d507e08a9d58aa8b6bddb Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Fri, 11 Sep 2026 20:58:25 -0500 Subject: [PATCH] fix(ui): make feed cards fully clickable (#13294) ## Thinking Path > - Paperclip helps people manage AI agents and their work. > - The activity feed shows cards for recent work. > - A feed card showed a linked entity, but only some card content behaved as one clear click target. > - Users expect the image, title, and blank card space to open the entity. > - This pull request makes the full visible card use the entity link. > - It also adds a regression test for the full-card click target. > - The benefit is a larger and consistent navigation target. ## Linked Issues or Issue Description **What happened?** Clicks on some feed card content or blank card space did not reliably open the linked entity. **Expected behavior** A click anywhere in a linked feed card opens its entity quick view. **Steps to reproduce** 1. Open the activity feed. 2. Find a card that links to an entity. 3. Click its image, title, or blank space. 4. Observe that only part of the card acts as the link before this change. **Paperclip version or commit** Current `master` before this pull request. ## What Changed - Made each linked feed card anchor use the full available width. - Made the visible card width account for its existing page margins. - Added a component test that clicks the card and confirms link navigation. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/FeedCard.test.tsx` - `pnpm check:token-gates` - `pnpm --filter @paperclipai/ui typecheck` ## Risks - Low risk. The change only expands the existing link click target. - The width calculation can affect feed card layout. The regression test checks the expected width classes. > This bug fix does not add a roadmap feature. ## Model Used - OpenAI GPT-5.6, Codex agent, with reasoning and code execution tools. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- ui/src/components/FeedCard.test.tsx | 73 +++++++++++++++++++++++++++++ ui/src/components/FeedCard.tsx | 5 +- 2 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 ui/src/components/FeedCard.test.tsx diff --git a/ui/src/components/FeedCard.test.tsx b/ui/src/components/FeedCard.test.tsx new file mode 100644 index 0000000000..a63418a835 --- /dev/null +++ b/ui/src/components/FeedCard.test.tsx @@ -0,0 +1,73 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot } from "react-dom/client"; +import type { ActivityEvent } from "@paperclipai/shared"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { FeedCard } from "./FeedCard"; + +const navigate = vi.fn(); + +vi.mock("@/lib/router", () => ({ + Link: ({ children, issueQuicklookSide: _, to, ...props }: React.ComponentProps<"a"> & { issueQuicklookSide?: string; to: string }) => ( + + {children} + + ), +})); + +const event: ActivityEvent = { + id: "event-1", + companyId: "company-1", + actorType: "user", + actorId: "user-1", + action: "issue.updated", + entityType: "issue", + entityId: "issue-1", + agentId: null, + runId: null, + details: null, + createdAt: new Date("2026-09-11T12:00:00.000Z"), +}; + +describe("FeedCard", () => { + let container: HTMLDivElement; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + navigate.mockClear(); + }); + + afterEach(() => { + container.remove(); + }); + + it("uses the whole visible card as the entity link", () => { + const root = createRoot(container); + act(() => { + root.render( + , + ); + }); + + const link = container.querySelector('[data-fc="link"]'); + const card = container.querySelector('[data-fc="card"]'); + + expect(link).not.toBeNull(); + expect(link?.className).toContain("w-full"); + expect(card?.className).toContain("w-(--sz-calc-1)"); + expect(card?.className).toContain("md:w-(--sz-calc-2)"); + expect(link?.contains(card ?? null)).toBe(true); + + card?.click(); + expect(navigate).toHaveBeenCalledOnce(); + + act(() => root.unmount()); + }); +}); diff --git a/ui/src/components/FeedCard.tsx b/ui/src/components/FeedCard.tsx index 614f299b34..e88c0bb9c7 100644 --- a/ui/src/components/FeedCard.tsx +++ b/ui/src/components/FeedCard.tsx @@ -437,7 +437,7 @@ export function FeedCard({ {card}