From 3eb57fce1f702cdf8658855bed708be21b67f3a7 Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 7 Jun 2026 01:28:23 +0000 Subject: [PATCH] Add Storybook stories for Documents library (PAP-10524) Standalone Populated/Empty/Mobile stories so UX/QA can capture library screenshots without a live backend. Co-Authored-By: Paperclip --- .../stories/documents-library.stories.tsx | 240 ++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100644 ui/storybook/stories/documents-library.stories.tsx diff --git a/ui/storybook/stories/documents-library.stories.tsx b/ui/storybook/stories/documents-library.stories.tsx new file mode 100644 index 00000000000..f08a0c3c291 --- /dev/null +++ b/ui/storybook/stories/documents-library.stories.tsx @@ -0,0 +1,240 @@ +import type { Meta, StoryObj } from "@storybook/react-vite"; +import { FileText, Search, SlidersHorizontal, X } from "lucide-react"; +import type { CompanyDocumentSummary } from "@paperclipai/shared"; +import { DocumentRow } from "@/components/documents/DocumentRow"; +import { EmptyState } from "@/components/EmptyState"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; + +/** + * Storybook coverage for the company Documents library (PAP-10524, slice 1). + * + * Each story renders the real `DocumentRow` component inside the library shell + * so UX/QA can capture desktop and mobile screenshots without booting a live + * backend. Filter controls are static here; behaviour is unit-tested in + * `Documents.test.tsx`. + */ + +function makeDocument(overrides: Partial): CompanyDocumentSummary { + return { + id: "doc", + companyId: "company-storybook", + title: "Untitled document", + format: "markdown", + status: "in_review", + documentType: "spec", + summary: null, + ownerAgentId: "agent-1", + ownerUserId: null, + latestRevisionId: "rev-1", + latestRevisionNumber: 1, + createdByAgentId: "agent-1", + createdByUserId: null, + updatedByAgentId: "agent-1", + updatedByUserId: null, + lockedAt: null, + lockedByAgentId: null, + lockedByUserId: null, + sourceTrust: null, + archivedAt: null, + archivedByAgentId: null, + archivedByUserId: null, + createdAt: new Date("2026-06-01T00:00:00Z"), + updatedAt: new Date("2026-06-07T00:00:00Z"), + backlinks: [], + feedbackCounts: { + openComments: 0, + resolvedComments: 0, + openReviewThreads: 0, + resolvedReviewThreads: 0, + pendingSuggestions: 0, + acceptedSuggestions: 0, + rejectedSuggestions: 0, + staleAnchors: 0, + orphanedAnchors: 0, + }, + ...overrides, + }; +} + +const HOUR = 1000 * 60 * 60; +const now = new Date("2026-06-07T12:00:00Z").getTime(); + +const SAMPLE_DOCS: { doc: CompanyDocumentSummary; owner: { name: string }; identifier?: string }[] = [ + { + doc: makeDocument({ + id: "doc-spec", + title: "Paperclip Documents review flow", + documentType: "spec", + status: "in_review", + summary: "Library + review surfaces, anchored comments, suggestions, handoff.", + updatedAt: new Date(now - 2 * HOUR), + backlinks: [ + { identifier: "PAP-10520" } as never, + { identifier: "PAP-10522" } as never, + ], + feedbackCounts: { + ...makeDocument({}).feedbackCounts, + openComments: 12, + pendingSuggestions: 3, + }, + }), + owner: { name: "ClaudeCoder" }, + identifier: "ux-spec", + }, + { + doc: makeDocument({ + id: "doc-plan", + title: "Onboarding for new agents", + documentType: "plan", + status: "draft", + summary: "Goal — get a new agent productive in their first heartbeat.", + updatedAt: new Date(now - 5 * HOUR), + feedbackCounts: { ...makeDocument({}).feedbackCounts, openComments: 4 }, + }), + owner: { name: "CEO" }, + identifier: "plan", + }, + { + doc: makeDocument({ + id: "doc-brief", + title: "Q3 hiring snapshot", + documentType: "brief", + status: "approved", + summary: "Headcount, open roles, and budget envelope for the quarter.", + updatedAt: new Date(now - 26 * HOUR), + }), + owner: { name: "CTO" }, + identifier: "q3-hiring", + }, + { + doc: makeDocument({ + id: "doc-report", + title: "Incident review — stalled review loops", + documentType: "report", + status: "in_review", + summary: "Why work stopped and the product rule that prevents recurrence.", + updatedAt: new Date(now - 3 * HOUR), + sourceTrust: { preset: "low", disposition: "quarantined" } as never, + feedbackCounts: { + ...makeDocument({}).feedbackCounts, + openReviewThreads: 2, + staleAnchors: 1, + }, + }), + owner: { name: "Researcher" }, + identifier: "incident-42", + }, + { + doc: makeDocument({ + id: "doc-other", + title: "Glossary of Paperclip terms", + documentType: "other", + status: "archived", + updatedAt: new Date(now - 40 * HOUR), + }), + owner: { name: "Docs Bot" }, + identifier: "glossary", + }, +]; + +function LibraryShell({ children }: { children: React.ReactNode }) { + return ( +
+
+
+
+ + +
+
+ + + + +
+
+
+ + Status: In review + + + +
+ {children} +
+
+ ); +} + +const meta: Meta = { + title: "Pages/Documents Library", + parameters: { layout: "fullscreen" }, +}; + +export default meta; +type Story = StoryObj; + +export const Populated: Story = { + render: () => ( + +
+ {SAMPLE_DOCS.map(({ doc, owner, identifier }) => ( + + ))} +
+
+ ), +}; + +export const Empty: Story = { + render: () => ( +
+
+ +
+
+ ), +}; + +export const Mobile: Story = { + parameters: { viewport: { defaultViewport: "mobile1" } }, + render: () => ( +
+
+
+ + +
+
+ {SAMPLE_DOCS.slice(0, 4).map(({ doc, owner, identifier }) => ( + + ))} +
+
+
+ ), +};