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 <noreply@paperclip.ing>
This commit is contained in:
Dotta 2026-09-11 20:58:25 -05:00 committed by GitHub
parent 09e208c54f
commit 30aa3740ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 76 additions and 2 deletions

View File

@ -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 }) => (
<a {...props} href={to} onClick={navigate}>
{children}
</a>
),
}));
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(
<FeedCard
event={event}
agentMap={new Map()}
entityNameMap={new Map([["issue:issue-1", "PAP-1"]])}
entityTitleMap={new Map([["issue:issue-1", "Clickable card"]])}
/>,
);
});
const link = container.querySelector<HTMLAnchorElement>('[data-fc="link"]');
const card = container.querySelector<HTMLElement>('[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());
});
});

View File

@ -437,7 +437,7 @@ export function FeedCard({
<Card
data-fc="card"
className={cn(
"flex-row group ml-3 mr-3 md:ml-0 my-2 items-center gap-2 p-(--sz-18px) text-xs",
"flex-row group ml-3 mr-3 md:ml-0 my-2 w-(--sz-calc-1) md:w-(--sz-calc-2) items-center gap-2 p-(--sz-18px) text-xs",
"transition-(--tp-background-color-border-color) duration-150",
content.link && "cursor-pointer hover:bg-accent hover:border-muted-foreground/30",
className,
@ -480,7 +480,8 @@ export function FeedCard({
return (
<Link
to={content.link}
className="block no-underline text-inherit"
data-fc="link"
className="block w-full no-underline text-inherit"
issueQuicklookSide="left"
>
{card}