This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / src / webapp / features / cards / components / urlCard / UrlCard.stories.tsx
7.4 kB 253 lines
1import type { Meta, StoryObj } from '@storybook/nextjs-vite'; 2import { useState } from 'react'; 3import { CollectionAccessType } from '@semble/types'; 4import type { Collection } from '@semble/types'; 5import UrlCard from './UrlCard'; 6import UrlCardSkeleton from './Skeleton.UrlCard'; 7import { AuthContext } from '@/hooks/useAuth'; 8import type { AuthContextType } from '@/hooks/useAuth'; 9 10const mockAuthUser = { 11 id: 'did:plc:mock123', 12 name: 'Test User', 13 handle: 'test.user', 14 avatarUrl: 'https://i.pravatar.cc/150?u=testuser', 15}; 16 17const mockAuthValue: AuthContextType = { 18 user: mockAuthUser as any, 19 isAuthenticated: true, 20 isLoading: false, 21 refreshAuth: async () => {}, 22 logout: async () => {}, 23}; 24 25const cardAuthor = { 26 id: 'did:plc:author456', 27 name: 'Jane Researcher', 28 handle: 'jane.researcher', 29 avatarUrl: 'https://i.pravatar.cc/150?u=jane', 30 description: 'Science writer and open-access advocate.', 31 followerCount: 214, 32 followingCount: 63, 33}; 34 35const collectionAuthor = { 36 id: 'did:plc:colauthor789', 37 name: 'Alex Curator', 38 handle: 'alex.curator', 39 avatarUrl: 'https://i.pravatar.cc/150?u=alex', 40}; 41 42const baseCollection: Collection = { 43 id: 'col-story-001', 44 uri: 'at://did:plc:colauthor789/app.semble.collection/3juzt2xkr5c2a', 45 name: 'Web Development Resources', 46 author: collectionAuthor, 47 description: 'A curated set of useful web development articles and tools.', 48 accessType: CollectionAccessType.CLOSED, 49 cardCount: 24, 50 createdAt: '2024-09-15T12:00:00.000Z', 51 updatedAt: '2025-06-20T09:30:00.000Z', 52 isFollowing: false, 53 followerCount: 42, 54}; 55 56const baseCardContent = { 57 url: 'https://example.com/understanding-design-tokens', 58 title: 'Understanding Design Tokens: A Practical Guide', 59 description: 60 'Design tokens are the atoms of a design system — small, named entities that store visual-design decisions such as color, typography, and spacing.', 61 imageUrl: 'https://picsum.photos/seed/urlcard1/400/225', 62 siteName: 'Example Blog', 63 type: 'article', 64 retrievedAt: '2025-06-01T10:00:00.000Z', 65}; 66 67const meta: Meta<typeof UrlCard> = { 68 title: 'Features/Cards/UrlCard', 69 component: UrlCard, 70 decorators: [ 71 (Story) => ( 72 <AuthContext value={mockAuthValue}> 73 <div style={{ maxWidth: 400 }}> 74 <Story /> 75 </div> 76 </AuthContext> 77 ), 78 ], 79 args: { 80 id: 'card-default-001', 81 url: 'https://example.com/understanding-design-tokens', 82 uri: 'at://did:plc:author456/app.semble.card/3k1abc', 83 cardContent: baseCardContent, 84 urlLibraryCount: 5, 85 urlIsInLibrary: false, 86 urlConnectionCount: 2, 87 urlIsConnected: false, 88 showAuthor: false, 89 }, 90}; 91 92export default meta; 93 94type Story = StoryObj<typeof UrlCard>; 95 96export const Default: Story = {}; 97 98export const WithContributor: Story = { 99 args: { 100 id: 'card-author-002', 101 showAuthor: true, 102 cardAuthor: cardAuthor, 103 authorHandle: cardAuthor.handle, 104 }, 105}; 106 107export const WithNote: Story = { 108 args: { 109 id: 'card-note-003', 110 note: { 111 id: 'note-001', 112 text: 'This is an excellent primer on design tokens. Highly recommend reading the section on naming conventions — it changed how I structure my own token files.', 113 }, 114 }, 115}; 116 117export const InLibrary: Story = { 118 args: { 119 id: 'card-library-004', 120 urlIsInLibrary: true, 121 urlLibraryCount: 37, 122 cardContent: { 123 ...baseCardContent, 124 url: 'https://example.com/state-of-css-2025', 125 title: 'State of CSS 2025 Survey Results', 126 description: 127 'An overview of the most popular CSS features, frameworks, and tools according to thousands of developers worldwide.', 128 imageUrl: 'https://picsum.photos/seed/urlcard2/400/225', 129 siteName: 'State of CSS', 130 }, 131 }, 132}; 133 134export const Connected: Story = { 135 args: { 136 id: 'card-connected-005', 137 urlIsConnected: true, 138 urlConnectionCount: 12, 139 cardContent: { 140 ...baseCardContent, 141 url: 'https://example.com/react-server-components', 142 title: 'React Server Components Explained', 143 description: 144 'A deep dive into how React Server Components work under the hood, when to use them, and common pitfalls to avoid.', 145 imageUrl: 'https://picsum.photos/seed/urlcard3/400/225', 146 siteName: 'React Blog', 147 }, 148 }, 149}; 150 151export const WithCollection: Story = { 152 args: { 153 id: 'card-collection-006', 154 currentCollection: baseCollection, 155 cardContent: { 156 ...baseCardContent, 157 url: 'https://example.com/responsive-layout-patterns', 158 title: 'Modern Responsive Layout Patterns', 159 description: 160 'Move beyond media queries. Learn how to build truly fluid layouts using CSS Grid, Container Queries, and clamp().', 161 imageUrl: 'https://picsum.photos/seed/urlcard4/400/225', 162 siteName: 'CSS Tricks', 163 }, 164 }, 165}; 166 167export const HighCounts: Story = { 168 args: { 169 id: 'card-highcounts-007', 170 urlLibraryCount: 2847, 171 urlIsInLibrary: true, 172 urlConnectionCount: 593, 173 urlIsConnected: true, 174 showAuthor: true, 175 cardAuthor: cardAuthor, 176 authorHandle: cardAuthor.handle, 177 cardContent: { 178 ...baseCardContent, 179 url: 'https://example.com/attention-is-all-you-need', 180 title: 'Attention Is All You Need', 181 description: 182 'The dominant sequence transduction models are based on complex recurrent or convolutional neural networks that include an encoder and a decoder.', 183 imageUrl: 'https://picsum.photos/seed/urlcard5/400/225', 184 siteName: 'arXiv', 185 author: 'Vaswani et al.', 186 }, 187 note: { 188 id: 'note-002', 189 text: 'The foundational transformer paper. Still worth re-reading every few months.', 190 }, 191 }, 192}; 193 194const ownedCollection: Collection = { 195 ...baseCollection, 196 id: 'col-story-pin-001', 197 uri: 'at://did:plc:mock123/app.semble.collection/pinstory', 198 name: 'My Reading List', 199 author: { 200 id: mockAuthUser.id, 201 name: mockAuthUser.name, 202 handle: mockAuthUser.handle, 203 avatarUrl: mockAuthUser.avatarUrl, 204 }, 205}; 206 207export const PinUnpinInCollection: Story = { 208 render: (args) => { 209 const [isPinned, setIsPinned] = useState(false); 210 return ( 211 <UrlCard 212 {...args} 213 isPinnedInCollection={isPinned} 214 onTogglePinInCollection={() => setIsPinned((prev) => !prev)} 215 /> 216 ); 217 }, 218 args: { 219 id: 'card-pin-toggle-008', 220 currentCollection: ownedCollection, 221 cardContent: { 222 ...baseCardContent, 223 url: 'https://example.com/css-cascade-layers', 224 title: 'A Practical Look at CSS Cascade Layers', 225 description: 226 'Cascade layers give you explicit control over CSS specificity ordering — here is when they help and when they get in the way.', 227 imageUrl: 'https://picsum.photos/seed/urlcard-pin/400/225', 228 siteName: 'Example Blog', 229 }, 230 }, 231}; 232 233export const PinnedInCollection: Story = { 234 args: { 235 id: 'card-pinned-009', 236 currentCollection: ownedCollection, 237 isPinnedInCollection: true, 238 onTogglePinInCollection: () => {}, 239 cardContent: { 240 ...baseCardContent, 241 url: 'https://example.com/the-tao-of-css', 242 title: 'The Tao of CSS', 243 description: 244 'A meditation on simplicity, specificity, and the long road from inline styles to design systems.', 245 imageUrl: 'https://picsum.photos/seed/urlcard-pinned/400/225', 246 siteName: 'Example Blog', 247 }, 248 }, 249}; 250 251export const Skeleton: Story = { 252 render: () => <UrlCardSkeleton />, 253};