@papernote/ui 1.8.2 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/NotificationBell.d.ts +104 -0
- package/dist/components/NotificationBell.d.ts.map +1 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/index.d.ts +106 -2
- package/dist/index.esm.js +236 -47
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +235 -45
- package/dist/index.js.map +1 -1
- package/dist/styles.css +21 -0
- package/package.json +1 -1
- package/src/components/Layout.tsx +1 -1
- package/src/components/NotificationBell.stories.tsx +717 -0
- package/src/components/NotificationBell.tsx +556 -0
- package/src/components/index.ts +4 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import NotificationBell, { NotificationItem } from './NotificationBell';
|
|
4
|
+
import Stack from './Stack';
|
|
5
|
+
import Text from './Text';
|
|
6
|
+
import Box from './Box';
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
title: 'Components/NotificationBell',
|
|
10
|
+
component: NotificationBell,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: 'centered',
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component: `
|
|
16
|
+
Notification bell component with badge and dropdown panel for displaying notifications.
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
- **Bell icon** with unread count badge
|
|
20
|
+
- **Two visual variants**: compact (dot indicator) and detailed (labeled badge)
|
|
21
|
+
- **Flexible positioning**: bottom-left, bottom-right, top-left, top-right
|
|
22
|
+
- **Notification items** with type badge, title, message, and time ago
|
|
23
|
+
- **Mark as read** actions (single and all)
|
|
24
|
+
- **View all** link to navigate to full notifications page
|
|
25
|
+
- **Loading state** with skeleton placeholders
|
|
26
|
+
- **Empty state** when no notifications
|
|
27
|
+
- **Keyboard accessible** (Escape to close, Enter to select)
|
|
28
|
+
|
|
29
|
+
## Variants
|
|
30
|
+
|
|
31
|
+
### Compact (default)
|
|
32
|
+
- Dot indicator for notification type
|
|
33
|
+
- Stacked layout: title, message, time
|
|
34
|
+
- Minimal footprint
|
|
35
|
+
|
|
36
|
+
### Detailed
|
|
37
|
+
- Labeled badge for notification type (e.g., "Alert", "Info")
|
|
38
|
+
- Time aligned right of title
|
|
39
|
+
- Shows "(X unread)" in header
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
\`\`\`tsx
|
|
44
|
+
import { NotificationBell } from 'notebook-ui';
|
|
45
|
+
|
|
46
|
+
// Compact variant (default)
|
|
47
|
+
<NotificationBell
|
|
48
|
+
notifications={notifications}
|
|
49
|
+
onMarkAsRead={(id) => markAsRead(id)}
|
|
50
|
+
onMarkAllRead={() => markAllRead()}
|
|
51
|
+
onNotificationClick={(n) => navigate(n.actionUrl)}
|
|
52
|
+
onViewAll={() => navigate('/notifications')}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
// Detailed variant
|
|
56
|
+
<NotificationBell
|
|
57
|
+
notifications={notifications}
|
|
58
|
+
variant="detailed"
|
|
59
|
+
showUnreadInHeader
|
|
60
|
+
dropdownPosition="bottom-left"
|
|
61
|
+
/>
|
|
62
|
+
\`\`\`
|
|
63
|
+
`,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
tags: ['autodocs'],
|
|
68
|
+
argTypes: {
|
|
69
|
+
variant: {
|
|
70
|
+
control: 'select',
|
|
71
|
+
options: ['compact', 'detailed'],
|
|
72
|
+
description: 'Visual variant - compact (dot) or detailed (labeled badge)',
|
|
73
|
+
table: {
|
|
74
|
+
type: { summary: 'compact | detailed' },
|
|
75
|
+
defaultValue: { summary: 'compact' },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
dropdownPosition: {
|
|
79
|
+
control: 'select',
|
|
80
|
+
options: ['bottom-left', 'bottom-right', 'top-left', 'top-right'],
|
|
81
|
+
description: 'Position of dropdown relative to bell',
|
|
82
|
+
table: {
|
|
83
|
+
type: { summary: 'bottom-left | bottom-right | top-left | top-right' },
|
|
84
|
+
defaultValue: { summary: 'bottom-right' },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
showUnreadInHeader: {
|
|
88
|
+
control: 'boolean',
|
|
89
|
+
description: 'Show unread count in header (e.g., "Notifications (2 unread)")',
|
|
90
|
+
table: {
|
|
91
|
+
type: { summary: 'boolean' },
|
|
92
|
+
defaultValue: { summary: 'false' },
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
size: {
|
|
96
|
+
control: 'select',
|
|
97
|
+
options: ['sm', 'md', 'lg'],
|
|
98
|
+
description: 'Size of the bell button',
|
|
99
|
+
table: {
|
|
100
|
+
type: { summary: 'sm | md | lg' },
|
|
101
|
+
defaultValue: { summary: 'md' },
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
maxHeight: {
|
|
105
|
+
control: 'text',
|
|
106
|
+
description: 'Maximum height of notification list before scrolling',
|
|
107
|
+
table: {
|
|
108
|
+
type: { summary: 'string' },
|
|
109
|
+
defaultValue: { summary: '400px' },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
loading: {
|
|
113
|
+
control: 'boolean',
|
|
114
|
+
description: 'Show loading skeleton state',
|
|
115
|
+
},
|
|
116
|
+
disabled: {
|
|
117
|
+
control: 'boolean',
|
|
118
|
+
description: 'Disable the bell button',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
} satisfies Meta<typeof NotificationBell>;
|
|
122
|
+
|
|
123
|
+
export default meta;
|
|
124
|
+
type Story = StoryObj<typeof meta>;
|
|
125
|
+
|
|
126
|
+
// Sample notifications for stories
|
|
127
|
+
const sampleNotifications: NotificationItem[] = [
|
|
128
|
+
{
|
|
129
|
+
id: '1',
|
|
130
|
+
title: 'Bill payment due',
|
|
131
|
+
message: 'Your electricity bill of $142.50 is due in 3 days',
|
|
132
|
+
type: 'warning',
|
|
133
|
+
priority: 'high',
|
|
134
|
+
createdAt: new Date(Date.now() - 5 * 60 * 1000),
|
|
135
|
+
isRead: false,
|
|
136
|
+
actionUrl: '/bills/electricity',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: '2',
|
|
140
|
+
title: 'Transfer completed',
|
|
141
|
+
message: 'Your transfer of $500 to John Smith has been completed successfully',
|
|
142
|
+
type: 'success',
|
|
143
|
+
priority: 'normal',
|
|
144
|
+
createdAt: new Date(Date.now() - 2 * 60 * 60 * 1000),
|
|
145
|
+
isRead: false,
|
|
146
|
+
actionUrl: '/transactions/123',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
id: '3',
|
|
150
|
+
title: 'Security alert',
|
|
151
|
+
message: 'New login detected from Chrome on Windows. If this was not you, please secure your account immediately.',
|
|
152
|
+
type: 'error',
|
|
153
|
+
priority: 'urgent',
|
|
154
|
+
createdAt: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
|
155
|
+
isRead: false,
|
|
156
|
+
actionUrl: '/security',
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: '4',
|
|
160
|
+
title: 'Monthly summary ready',
|
|
161
|
+
message: 'Your January spending summary is now available',
|
|
162
|
+
type: 'info',
|
|
163
|
+
priority: 'low',
|
|
164
|
+
createdAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000),
|
|
165
|
+
isRead: true,
|
|
166
|
+
actionUrl: '/reports/january',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: '5',
|
|
170
|
+
title: 'Budget alert',
|
|
171
|
+
message: 'You have reached 80% of your monthly dining budget',
|
|
172
|
+
type: 'warning',
|
|
173
|
+
priority: 'normal',
|
|
174
|
+
createdAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000),
|
|
175
|
+
isRead: true,
|
|
176
|
+
actionUrl: '/budgets/dining',
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
// Banking-style notifications for detailed variant
|
|
181
|
+
const bankingNotifications: NotificationItem[] = [
|
|
182
|
+
{
|
|
183
|
+
id: '1',
|
|
184
|
+
title: 'Low balance in SAVINGS 9878',
|
|
185
|
+
message: 'Your SAVINGS 9878 account balance ($6.62) is below your threshold of $100.00.',
|
|
186
|
+
type: 'error',
|
|
187
|
+
typeLabel: 'Alert',
|
|
188
|
+
priority: 'high',
|
|
189
|
+
createdAt: new Date(Date.now() - 35 * 60 * 1000),
|
|
190
|
+
isRead: false,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: '2',
|
|
194
|
+
title: 'Low balance in CHECKING 4364',
|
|
195
|
+
message: 'Your CHECKING 4364 account balance ($56.54) is below your threshold of $100.00.',
|
|
196
|
+
type: 'error',
|
|
197
|
+
typeLabel: 'Alert',
|
|
198
|
+
priority: 'high',
|
|
199
|
+
createdAt: new Date(Date.now() - 35 * 60 * 1000),
|
|
200
|
+
isRead: false,
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
export const Default: Story = {
|
|
205
|
+
render: () => {
|
|
206
|
+
const [notifications, setNotifications] = useState(sampleNotifications);
|
|
207
|
+
|
|
208
|
+
const handleMarkAsRead = (id: string) => {
|
|
209
|
+
setNotifications((prev) =>
|
|
210
|
+
prev.map((n) => (n.id === id ? { ...n, isRead: true } : n))
|
|
211
|
+
);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const handleMarkAllRead = () => {
|
|
215
|
+
setNotifications((prev) => prev.map((n) => ({ ...n, isRead: true })));
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<NotificationBell
|
|
220
|
+
notifications={notifications}
|
|
221
|
+
onMarkAsRead={handleMarkAsRead}
|
|
222
|
+
onMarkAllRead={handleMarkAllRead}
|
|
223
|
+
onNotificationClick={(n) => alert(`Navigate to: ${n.actionUrl}`)}
|
|
224
|
+
onViewAll={() => alert('View all notifications')}
|
|
225
|
+
/>
|
|
226
|
+
);
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
export const DetailedVariant: Story = {
|
|
231
|
+
render: () => {
|
|
232
|
+
const [notifications, setNotifications] = useState(bankingNotifications);
|
|
233
|
+
|
|
234
|
+
const handleMarkAsRead = (id: string) => {
|
|
235
|
+
setNotifications((prev) =>
|
|
236
|
+
prev.map((n) => (n.id === id ? { ...n, isRead: true } : n))
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const handleMarkAllRead = () => {
|
|
241
|
+
setNotifications((prev) => prev.map((n) => ({ ...n, isRead: true })));
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<NotificationBell
|
|
246
|
+
notifications={notifications}
|
|
247
|
+
variant="detailed"
|
|
248
|
+
showUnreadInHeader
|
|
249
|
+
onMarkAsRead={handleMarkAsRead}
|
|
250
|
+
onMarkAllRead={handleMarkAllRead}
|
|
251
|
+
onNotificationClick={(n) => alert(`Clicked: ${n.title}`)}
|
|
252
|
+
onViewAll={() => alert('View all notifications')}
|
|
253
|
+
/>
|
|
254
|
+
);
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const CompactVsDetailed: Story = {
|
|
259
|
+
render: () => {
|
|
260
|
+
const notifications = sampleNotifications.slice(0, 3);
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<Stack direction="horizontal" spacing="xl" align="start">
|
|
264
|
+
<Stack align="center" spacing="sm">
|
|
265
|
+
<NotificationBell
|
|
266
|
+
notifications={notifications}
|
|
267
|
+
variant="compact"
|
|
268
|
+
onViewAll={() => {}}
|
|
269
|
+
/>
|
|
270
|
+
<Text size="xs" color="muted">Compact</Text>
|
|
271
|
+
</Stack>
|
|
272
|
+
<Stack align="center" spacing="sm">
|
|
273
|
+
<NotificationBell
|
|
274
|
+
notifications={notifications}
|
|
275
|
+
variant="detailed"
|
|
276
|
+
showUnreadInHeader
|
|
277
|
+
onViewAll={() => {}}
|
|
278
|
+
/>
|
|
279
|
+
<Text size="xs" color="muted">Detailed</Text>
|
|
280
|
+
</Stack>
|
|
281
|
+
</Stack>
|
|
282
|
+
);
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
export const DropdownPositions: Story = {
|
|
287
|
+
render: () => (
|
|
288
|
+
<Box padding="xl">
|
|
289
|
+
<div style={{
|
|
290
|
+
display: 'grid',
|
|
291
|
+
gridTemplateColumns: '1fr 1fr',
|
|
292
|
+
gap: '200px',
|
|
293
|
+
padding: '100px',
|
|
294
|
+
}}>
|
|
295
|
+
<Stack align="start" spacing="xs">
|
|
296
|
+
<NotificationBell
|
|
297
|
+
notifications={sampleNotifications.slice(0, 2)}
|
|
298
|
+
dropdownPosition="bottom-right"
|
|
299
|
+
onViewAll={() => {}}
|
|
300
|
+
/>
|
|
301
|
+
<Text size="xs" color="muted">bottom-right</Text>
|
|
302
|
+
</Stack>
|
|
303
|
+
<Stack align="end" spacing="xs">
|
|
304
|
+
<NotificationBell
|
|
305
|
+
notifications={sampleNotifications.slice(0, 2)}
|
|
306
|
+
dropdownPosition="bottom-left"
|
|
307
|
+
onViewAll={() => {}}
|
|
308
|
+
/>
|
|
309
|
+
<Text size="xs" color="muted">bottom-left</Text>
|
|
310
|
+
</Stack>
|
|
311
|
+
<Stack align="start" spacing="xs">
|
|
312
|
+
<Text size="xs" color="muted">top-right</Text>
|
|
313
|
+
<NotificationBell
|
|
314
|
+
notifications={sampleNotifications.slice(0, 2)}
|
|
315
|
+
dropdownPosition="top-right"
|
|
316
|
+
onViewAll={() => {}}
|
|
317
|
+
/>
|
|
318
|
+
</Stack>
|
|
319
|
+
<Stack align="end" spacing="xs">
|
|
320
|
+
<Text size="xs" color="muted">top-left</Text>
|
|
321
|
+
<NotificationBell
|
|
322
|
+
notifications={sampleNotifications.slice(0, 2)}
|
|
323
|
+
dropdownPosition="top-left"
|
|
324
|
+
onViewAll={() => {}}
|
|
325
|
+
/>
|
|
326
|
+
</Stack>
|
|
327
|
+
</div>
|
|
328
|
+
</Box>
|
|
329
|
+
),
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export const WithUnreadBadge: Story = {
|
|
333
|
+
render: () => (
|
|
334
|
+
<Stack direction="horizontal" spacing="lg" align="center">
|
|
335
|
+
<Stack align="center" spacing="xs">
|
|
336
|
+
<NotificationBell
|
|
337
|
+
notifications={sampleNotifications}
|
|
338
|
+
unreadCount={3}
|
|
339
|
+
/>
|
|
340
|
+
<Text size="xs" color="muted">3 unread</Text>
|
|
341
|
+
</Stack>
|
|
342
|
+
<Stack align="center" spacing="xs">
|
|
343
|
+
<NotificationBell
|
|
344
|
+
notifications={sampleNotifications}
|
|
345
|
+
unreadCount={99}
|
|
346
|
+
/>
|
|
347
|
+
<Text size="xs" color="muted">99 unread</Text>
|
|
348
|
+
</Stack>
|
|
349
|
+
<Stack align="center" spacing="xs">
|
|
350
|
+
<NotificationBell
|
|
351
|
+
notifications={sampleNotifications}
|
|
352
|
+
unreadCount={150}
|
|
353
|
+
/>
|
|
354
|
+
<Text size="xs" color="muted">99+ unread</Text>
|
|
355
|
+
</Stack>
|
|
356
|
+
</Stack>
|
|
357
|
+
),
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export const Sizes: Story = {
|
|
361
|
+
render: () => (
|
|
362
|
+
<Stack direction="horizontal" spacing="lg" align="center">
|
|
363
|
+
<Stack align="center" spacing="xs">
|
|
364
|
+
<NotificationBell
|
|
365
|
+
notifications={sampleNotifications}
|
|
366
|
+
unreadCount={5}
|
|
367
|
+
size="sm"
|
|
368
|
+
/>
|
|
369
|
+
<Text size="xs" color="muted">Small</Text>
|
|
370
|
+
</Stack>
|
|
371
|
+
<Stack align="center" spacing="xs">
|
|
372
|
+
<NotificationBell
|
|
373
|
+
notifications={sampleNotifications}
|
|
374
|
+
unreadCount={5}
|
|
375
|
+
size="md"
|
|
376
|
+
/>
|
|
377
|
+
<Text size="xs" color="muted">Medium</Text>
|
|
378
|
+
</Stack>
|
|
379
|
+
<Stack align="center" spacing="xs">
|
|
380
|
+
<NotificationBell
|
|
381
|
+
notifications={sampleNotifications}
|
|
382
|
+
unreadCount={5}
|
|
383
|
+
size="lg"
|
|
384
|
+
/>
|
|
385
|
+
<Text size="xs" color="muted">Large</Text>
|
|
386
|
+
</Stack>
|
|
387
|
+
</Stack>
|
|
388
|
+
),
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
export const EmptyState: Story = {
|
|
392
|
+
render: () => (
|
|
393
|
+
<Stack direction="horizontal" spacing="xl">
|
|
394
|
+
<Stack align="center" spacing="xs">
|
|
395
|
+
<NotificationBell
|
|
396
|
+
notifications={[]}
|
|
397
|
+
variant="compact"
|
|
398
|
+
onViewAll={() => {}}
|
|
399
|
+
/>
|
|
400
|
+
<Text size="xs" color="muted">Compact</Text>
|
|
401
|
+
</Stack>
|
|
402
|
+
<Stack align="center" spacing="xs">
|
|
403
|
+
<NotificationBell
|
|
404
|
+
notifications={[]}
|
|
405
|
+
variant="detailed"
|
|
406
|
+
onViewAll={() => {}}
|
|
407
|
+
/>
|
|
408
|
+
<Text size="xs" color="muted">Detailed</Text>
|
|
409
|
+
</Stack>
|
|
410
|
+
</Stack>
|
|
411
|
+
),
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
export const LoadingState: Story = {
|
|
415
|
+
render: () => (
|
|
416
|
+
<Stack direction="horizontal" spacing="xl">
|
|
417
|
+
<Stack align="center" spacing="xs">
|
|
418
|
+
<NotificationBell
|
|
419
|
+
notifications={[]}
|
|
420
|
+
loading
|
|
421
|
+
variant="compact"
|
|
422
|
+
/>
|
|
423
|
+
<Text size="xs" color="muted">Compact</Text>
|
|
424
|
+
</Stack>
|
|
425
|
+
<Stack align="center" spacing="xs">
|
|
426
|
+
<NotificationBell
|
|
427
|
+
notifications={[]}
|
|
428
|
+
loading
|
|
429
|
+
variant="detailed"
|
|
430
|
+
/>
|
|
431
|
+
<Text size="xs" color="muted">Detailed</Text>
|
|
432
|
+
</Stack>
|
|
433
|
+
</Stack>
|
|
434
|
+
),
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
export const NotificationTypes: Story = {
|
|
438
|
+
render: () => {
|
|
439
|
+
const typeNotifications: NotificationItem[] = [
|
|
440
|
+
{
|
|
441
|
+
id: '1',
|
|
442
|
+
title: 'Info notification',
|
|
443
|
+
message: 'This is an informational message',
|
|
444
|
+
type: 'info',
|
|
445
|
+
priority: 'normal',
|
|
446
|
+
createdAt: new Date(),
|
|
447
|
+
isRead: false,
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
id: '2',
|
|
451
|
+
title: 'Success notification',
|
|
452
|
+
message: 'Operation completed successfully',
|
|
453
|
+
type: 'success',
|
|
454
|
+
priority: 'normal',
|
|
455
|
+
createdAt: new Date(),
|
|
456
|
+
isRead: false,
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
id: '3',
|
|
460
|
+
title: 'Warning notification',
|
|
461
|
+
message: 'Please review this item',
|
|
462
|
+
type: 'warning',
|
|
463
|
+
priority: 'high',
|
|
464
|
+
createdAt: new Date(),
|
|
465
|
+
isRead: false,
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
id: '4',
|
|
469
|
+
title: 'Error notification',
|
|
470
|
+
message: 'Something went wrong',
|
|
471
|
+
type: 'error',
|
|
472
|
+
priority: 'urgent',
|
|
473
|
+
createdAt: new Date(),
|
|
474
|
+
isRead: false,
|
|
475
|
+
},
|
|
476
|
+
];
|
|
477
|
+
|
|
478
|
+
return (
|
|
479
|
+
<Stack direction="horizontal" spacing="xl">
|
|
480
|
+
<Stack align="center" spacing="xs">
|
|
481
|
+
<NotificationBell
|
|
482
|
+
notifications={typeNotifications}
|
|
483
|
+
variant="compact"
|
|
484
|
+
/>
|
|
485
|
+
<Text size="xs" color="muted">Compact</Text>
|
|
486
|
+
</Stack>
|
|
487
|
+
<Stack align="center" spacing="xs">
|
|
488
|
+
<NotificationBell
|
|
489
|
+
notifications={typeNotifications}
|
|
490
|
+
variant="detailed"
|
|
491
|
+
showUnreadInHeader
|
|
492
|
+
/>
|
|
493
|
+
<Text size="xs" color="muted">Detailed</Text>
|
|
494
|
+
</Stack>
|
|
495
|
+
</Stack>
|
|
496
|
+
);
|
|
497
|
+
},
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
export const CustomTypeLabels: Story = {
|
|
501
|
+
render: () => {
|
|
502
|
+
const customNotifications: NotificationItem[] = [
|
|
503
|
+
{
|
|
504
|
+
id: '1',
|
|
505
|
+
title: 'Account Update',
|
|
506
|
+
message: 'Your profile has been updated',
|
|
507
|
+
type: 'info',
|
|
508
|
+
typeLabel: 'Update',
|
|
509
|
+
priority: 'normal',
|
|
510
|
+
createdAt: new Date(),
|
|
511
|
+
isRead: false,
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
id: '2',
|
|
515
|
+
title: 'Payment Received',
|
|
516
|
+
message: 'You received $500 from John',
|
|
517
|
+
type: 'success',
|
|
518
|
+
typeLabel: 'Payment',
|
|
519
|
+
priority: 'normal',
|
|
520
|
+
createdAt: new Date(),
|
|
521
|
+
isRead: false,
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
id: '3',
|
|
525
|
+
title: 'Low Balance',
|
|
526
|
+
message: 'Your checking account is below $100',
|
|
527
|
+
type: 'error',
|
|
528
|
+
typeLabel: 'Alert',
|
|
529
|
+
priority: 'high',
|
|
530
|
+
createdAt: new Date(),
|
|
531
|
+
isRead: false,
|
|
532
|
+
},
|
|
533
|
+
];
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
<NotificationBell
|
|
537
|
+
notifications={customNotifications}
|
|
538
|
+
variant="detailed"
|
|
539
|
+
showUnreadInHeader
|
|
540
|
+
onViewAll={() => {}}
|
|
541
|
+
/>
|
|
542
|
+
);
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
|
|
546
|
+
export const ManyNotifications: Story = {
|
|
547
|
+
render: () => {
|
|
548
|
+
const manyNotifications: NotificationItem[] = Array.from(
|
|
549
|
+
{ length: 20 },
|
|
550
|
+
(_, i) => ({
|
|
551
|
+
id: String(i + 1),
|
|
552
|
+
title: `Notification ${i + 1}`,
|
|
553
|
+
message: `This is the message for notification ${i + 1}`,
|
|
554
|
+
type: (['info', 'success', 'warning', 'error'] as const)[i % 4],
|
|
555
|
+
priority: (['low', 'normal', 'high', 'urgent'] as const)[i % 4],
|
|
556
|
+
createdAt: new Date(Date.now() - i * 60 * 60 * 1000),
|
|
557
|
+
isRead: i > 5,
|
|
558
|
+
})
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
return (
|
|
562
|
+
<NotificationBell
|
|
563
|
+
notifications={manyNotifications}
|
|
564
|
+
maxHeight="300px"
|
|
565
|
+
onViewAll={() => alert('View all 20 notifications')}
|
|
566
|
+
/>
|
|
567
|
+
);
|
|
568
|
+
},
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
export const InHeader: Story = {
|
|
572
|
+
render: () => {
|
|
573
|
+
const [notifications, setNotifications] = useState(bankingNotifications);
|
|
574
|
+
|
|
575
|
+
return (
|
|
576
|
+
<div
|
|
577
|
+
style={{
|
|
578
|
+
display: 'flex',
|
|
579
|
+
alignItems: 'center',
|
|
580
|
+
justifyContent: 'space-between',
|
|
581
|
+
padding: '0.75rem 1.5rem',
|
|
582
|
+
backgroundColor: 'white',
|
|
583
|
+
borderBottom: '1px solid #e5e7eb',
|
|
584
|
+
width: '500px',
|
|
585
|
+
}}
|
|
586
|
+
>
|
|
587
|
+
<Text weight="semibold">Prylance</Text>
|
|
588
|
+
<NotificationBell
|
|
589
|
+
notifications={notifications}
|
|
590
|
+
variant="detailed"
|
|
591
|
+
showUnreadInHeader
|
|
592
|
+
dropdownPosition="bottom-left"
|
|
593
|
+
onMarkAsRead={(id) =>
|
|
594
|
+
setNotifications((prev) =>
|
|
595
|
+
prev.map((n) => (n.id === id ? { ...n, isRead: true } : n))
|
|
596
|
+
)
|
|
597
|
+
}
|
|
598
|
+
onMarkAllRead={() =>
|
|
599
|
+
setNotifications((prev) =>
|
|
600
|
+
prev.map((n) => ({ ...n, isRead: true }))
|
|
601
|
+
)
|
|
602
|
+
}
|
|
603
|
+
onViewAll={() => alert('View all')}
|
|
604
|
+
/>
|
|
605
|
+
</div>
|
|
606
|
+
);
|
|
607
|
+
},
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
export const Disabled: Story = {
|
|
611
|
+
render: () => (
|
|
612
|
+
<NotificationBell
|
|
613
|
+
notifications={sampleNotifications}
|
|
614
|
+
unreadCount={5}
|
|
615
|
+
disabled
|
|
616
|
+
/>
|
|
617
|
+
),
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
export const BellStyles: Story = {
|
|
621
|
+
render: () => (
|
|
622
|
+
<Stack direction="horizontal" spacing="xl" align="center">
|
|
623
|
+
<Stack align="center" spacing="sm">
|
|
624
|
+
<NotificationBell
|
|
625
|
+
notifications={sampleNotifications}
|
|
626
|
+
unreadCount={2}
|
|
627
|
+
bellStyle="ghost"
|
|
628
|
+
onViewAll={() => {}}
|
|
629
|
+
/>
|
|
630
|
+
<Text size="xs" color="muted">Ghost (default)</Text>
|
|
631
|
+
</Stack>
|
|
632
|
+
<Stack align="center" spacing="sm">
|
|
633
|
+
<NotificationBell
|
|
634
|
+
notifications={sampleNotifications}
|
|
635
|
+
unreadCount={2}
|
|
636
|
+
bellStyle="outlined"
|
|
637
|
+
onViewAll={() => {}}
|
|
638
|
+
/>
|
|
639
|
+
<Text size="xs" color="muted">Outlined</Text>
|
|
640
|
+
</Stack>
|
|
641
|
+
</Stack>
|
|
642
|
+
),
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
export const OutlinedSizes: Story = {
|
|
646
|
+
render: () => (
|
|
647
|
+
<Stack direction="horizontal" spacing="xl" align="center">
|
|
648
|
+
<Stack align="center" spacing="sm">
|
|
649
|
+
<NotificationBell
|
|
650
|
+
notifications={sampleNotifications}
|
|
651
|
+
unreadCount={3}
|
|
652
|
+
bellStyle="outlined"
|
|
653
|
+
size="sm"
|
|
654
|
+
/>
|
|
655
|
+
<Text size="xs" color="muted">Small</Text>
|
|
656
|
+
</Stack>
|
|
657
|
+
<Stack align="center" spacing="sm">
|
|
658
|
+
<NotificationBell
|
|
659
|
+
notifications={sampleNotifications}
|
|
660
|
+
unreadCount={3}
|
|
661
|
+
bellStyle="outlined"
|
|
662
|
+
size="md"
|
|
663
|
+
/>
|
|
664
|
+
<Text size="xs" color="muted">Medium</Text>
|
|
665
|
+
</Stack>
|
|
666
|
+
<Stack align="center" spacing="sm">
|
|
667
|
+
<NotificationBell
|
|
668
|
+
notifications={sampleNotifications}
|
|
669
|
+
unreadCount={3}
|
|
670
|
+
bellStyle="outlined"
|
|
671
|
+
size="lg"
|
|
672
|
+
/>
|
|
673
|
+
<Text size="xs" color="muted">Large</Text>
|
|
674
|
+
</Stack>
|
|
675
|
+
</Stack>
|
|
676
|
+
),
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
export const OutlinedInHeader: Story = {
|
|
680
|
+
render: () => {
|
|
681
|
+
const [notifications, setNotifications] = useState(bankingNotifications);
|
|
682
|
+
|
|
683
|
+
return (
|
|
684
|
+
<div
|
|
685
|
+
style={{
|
|
686
|
+
display: 'flex',
|
|
687
|
+
alignItems: 'center',
|
|
688
|
+
justifyContent: 'space-between',
|
|
689
|
+
padding: '0.75rem 1.5rem',
|
|
690
|
+
backgroundColor: '#fafaf9',
|
|
691
|
+
borderBottom: '1px solid #e5e7eb',
|
|
692
|
+
width: '500px',
|
|
693
|
+
}}
|
|
694
|
+
>
|
|
695
|
+
<Text weight="semibold">Prylance</Text>
|
|
696
|
+
<NotificationBell
|
|
697
|
+
notifications={notifications}
|
|
698
|
+
bellStyle="outlined"
|
|
699
|
+
variant="detailed"
|
|
700
|
+
showUnreadInHeader
|
|
701
|
+
dropdownPosition="bottom-left"
|
|
702
|
+
onMarkAsRead={(id) =>
|
|
703
|
+
setNotifications((prev) =>
|
|
704
|
+
prev.map((n) => (n.id === id ? { ...n, isRead: true } : n))
|
|
705
|
+
)
|
|
706
|
+
}
|
|
707
|
+
onMarkAllRead={() =>
|
|
708
|
+
setNotifications((prev) =>
|
|
709
|
+
prev.map((n) => ({ ...n, isRead: true }))
|
|
710
|
+
)
|
|
711
|
+
}
|
|
712
|
+
onViewAll={() => alert('View all')}
|
|
713
|
+
/>
|
|
714
|
+
</div>
|
|
715
|
+
);
|
|
716
|
+
},
|
|
717
|
+
};
|