@mdxui/zero 6.0.0
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/.storybook/preview.ts +20 -0
- package/.turbo/turbo-typecheck.log +5 -0
- package/ARCHITECTURE.md +415 -0
- package/CHANGELOG.md +80 -0
- package/README.md +205 -0
- package/package.json +43 -0
- package/playwright.config.ts +55 -0
- package/src/components/index.ts +20 -0
- package/src/compose/email-composer.stories.tsx +219 -0
- package/src/compose/email-composer.tsx +619 -0
- package/src/compose/index.ts +14 -0
- package/src/dashboard/index.ts +14 -0
- package/src/dashboard/mail-shell.stories.tsx +272 -0
- package/src/dashboard/mail-shell.tsx +199 -0
- package/src/dashboard/mail-sidebar.stories.tsx +158 -0
- package/src/dashboard/mail-sidebar.tsx +388 -0
- package/src/index.ts +24 -0
- package/src/landing/index.ts +24 -0
- package/src/mail/index.ts +15 -0
- package/src/mail/mail-item.stories.tsx +422 -0
- package/src/mail/mail-item.tsx +229 -0
- package/src/mail/mail-list.stories.tsx +320 -0
- package/src/mail/mail-list.tsx +262 -0
- package/src/mail/message-view.stories.tsx +459 -0
- package/src/mail/message-view.tsx +378 -0
- package/src/mail/thread-display.stories.tsx +260 -0
- package/src/mail/thread-display.tsx +392 -0
- package/src/pages/index.ts +9 -0
- package/src/pages/mail-zero-page.stories.tsx +251 -0
- package/src/pages/mail-zero-page.tsx +334 -0
- package/tests/visual/report/index.html +85 -0
- package/tests/visual/snapshots/zero-components.spec.ts/mail-shell-default.png +0 -0
- package/tests/visual/zero-components.spec.ts +321 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import type { Message } from "mdxui";
|
|
3
|
+
import { MessageHeader, MessageView } from "./message-view";
|
|
4
|
+
|
|
5
|
+
// Sample messages for testing different MessageView states
|
|
6
|
+
const simpleMessage: Message = {
|
|
7
|
+
id: "m1",
|
|
8
|
+
threadId: "t1",
|
|
9
|
+
subject: "Quick question about the project",
|
|
10
|
+
from: { address: "sarah@company.com", name: "Sarah Chen" },
|
|
11
|
+
to: [{ address: "me@company.com", name: "Me" }],
|
|
12
|
+
date: new Date(Date.now() - 3600000).toISOString(),
|
|
13
|
+
snippet: "Hey, just wanted to check in on the project timeline.",
|
|
14
|
+
textBody: `Hey,
|
|
15
|
+
|
|
16
|
+
Just wanted to check in on the project timeline. Are we still on track for the Q4 launch?
|
|
17
|
+
|
|
18
|
+
Let me know if you need anything from my end.
|
|
19
|
+
|
|
20
|
+
Thanks,
|
|
21
|
+
Sarah`,
|
|
22
|
+
attachments: [],
|
|
23
|
+
labels: [],
|
|
24
|
+
isRead: true,
|
|
25
|
+
isStarred: false,
|
|
26
|
+
isImportant: false,
|
|
27
|
+
isDraft: false,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const richHtmlMessage: Message = {
|
|
31
|
+
id: "m2",
|
|
32
|
+
threadId: "t2",
|
|
33
|
+
subject: "Product Launch Strategy - Q1 2025",
|
|
34
|
+
from: { address: "alex@startup.io", name: "Alex Rivera" },
|
|
35
|
+
to: [{ address: "me@company.com", name: "Me" }],
|
|
36
|
+
cc: [
|
|
37
|
+
{ address: "marketing@startup.io", name: "Marketing Team" },
|
|
38
|
+
{ address: "sales@startup.io", name: "Sales Team" },
|
|
39
|
+
],
|
|
40
|
+
date: new Date(Date.now() - 7200000).toISOString(),
|
|
41
|
+
snippet:
|
|
42
|
+
"Great ideas! I think we should focus on the enterprise segment first...",
|
|
43
|
+
textBody: `Hi there,
|
|
44
|
+
|
|
45
|
+
Thanks for sharing the product launch strategy. I've reviewed the document and have some thoughts:
|
|
46
|
+
|
|
47
|
+
1. **Enterprise Focus**: I strongly agree we should target enterprise customers first. They have the budget and the pain points our product solves.
|
|
48
|
+
|
|
49
|
+
2. **Timeline**: Q1 might be aggressive given the holiday freeze. Consider a soft launch in late January with full GA in February.
|
|
50
|
+
|
|
51
|
+
3. **Marketing Channels**:
|
|
52
|
+
- LinkedIn ads for B2B reach
|
|
53
|
+
- Content marketing with case studies
|
|
54
|
+
- Webinar series with industry experts
|
|
55
|
+
|
|
56
|
+
Let me know what you think about these suggestions. Happy to hop on a call to discuss further.
|
|
57
|
+
|
|
58
|
+
Best,
|
|
59
|
+
Alex`,
|
|
60
|
+
htmlBody: `<div>
|
|
61
|
+
<p>Hi there,</p>
|
|
62
|
+
<p>Thanks for sharing the product launch strategy. I've reviewed the document and have some thoughts:</p>
|
|
63
|
+
<ol>
|
|
64
|
+
<li><strong>Enterprise Focus</strong>: I strongly agree we should target enterprise customers first. They have the budget and the pain points our product solves.</li>
|
|
65
|
+
<li><strong>Timeline</strong>: Q1 might be aggressive given the holiday freeze. Consider a soft launch in late January with full GA in February.</li>
|
|
66
|
+
<li><strong>Marketing Channels</strong>:
|
|
67
|
+
<ul>
|
|
68
|
+
<li>LinkedIn ads for B2B reach</li>
|
|
69
|
+
<li>Content marketing with case studies</li>
|
|
70
|
+
<li>Webinar series with industry experts</li>
|
|
71
|
+
</ul>
|
|
72
|
+
</li>
|
|
73
|
+
</ol>
|
|
74
|
+
<p>Let me know what you think about these suggestions. Happy to hop on a call to discuss further.</p>
|
|
75
|
+
<p>Best,<br/>Alex</p>
|
|
76
|
+
</div>`,
|
|
77
|
+
attachments: [
|
|
78
|
+
{
|
|
79
|
+
id: "a1",
|
|
80
|
+
filename: "launch-timeline.pdf",
|
|
81
|
+
mimeType: "application/pdf",
|
|
82
|
+
size: 245000,
|
|
83
|
+
url: "#",
|
|
84
|
+
inline: false,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "a2",
|
|
88
|
+
filename: "market-research.xlsx",
|
|
89
|
+
mimeType:
|
|
90
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
91
|
+
size: 89000,
|
|
92
|
+
url: "#",
|
|
93
|
+
inline: false,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
labels: [{ id: "l1", name: "Important", color: "#ef4444", type: "user" }],
|
|
97
|
+
isRead: true,
|
|
98
|
+
isStarred: true,
|
|
99
|
+
isImportant: true,
|
|
100
|
+
isDraft: false,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const starredMessage: Message = {
|
|
104
|
+
id: "m3",
|
|
105
|
+
threadId: "t3",
|
|
106
|
+
subject: "Critical: Production Issue Alert",
|
|
107
|
+
from: { address: "alerts@monitoring.io", name: "Monitoring System" },
|
|
108
|
+
to: [{ address: "oncall@company.com", name: "On-Call Team" }],
|
|
109
|
+
date: new Date(Date.now() - 1800000).toISOString(),
|
|
110
|
+
snippet: "Error rate increased to 15% in the last 5 minutes...",
|
|
111
|
+
textBody: `ALERT: High Error Rate Detected
|
|
112
|
+
|
|
113
|
+
Service: api.example.com.ai
|
|
114
|
+
Error Rate: 15%
|
|
115
|
+
Duration: 5 minutes
|
|
116
|
+
Severity: Critical
|
|
117
|
+
|
|
118
|
+
Please investigate immediately.
|
|
119
|
+
|
|
120
|
+
View dashboard: https://monitoring.io/dashboard
|
|
121
|
+
View logs: https://monitoring.io/logs`,
|
|
122
|
+
attachments: [],
|
|
123
|
+
labels: [
|
|
124
|
+
{ id: "l2", name: "Urgent", color: "#ef4444", type: "user" },
|
|
125
|
+
{ id: "l3", name: "Production", color: "#f59e0b", type: "system" },
|
|
126
|
+
],
|
|
127
|
+
isRead: false,
|
|
128
|
+
isStarred: true,
|
|
129
|
+
isImportant: true,
|
|
130
|
+
isDraft: false,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const messageWithImages: Message = {
|
|
134
|
+
id: "m4",
|
|
135
|
+
threadId: "t4",
|
|
136
|
+
subject: "Design Mockups - Homepage Redesign",
|
|
137
|
+
from: { address: "mike@design.co", name: "Mike Johnson" },
|
|
138
|
+
to: [{ address: "me@company.com", name: "Me" }],
|
|
139
|
+
date: new Date(Date.now() - 43200000).toISOString(),
|
|
140
|
+
snippet: "Here are the updated mockups for the homepage redesign.",
|
|
141
|
+
textBody:
|
|
142
|
+
"Here are the updated mockups for the homepage redesign. Let me know what you think!",
|
|
143
|
+
attachments: [
|
|
144
|
+
{
|
|
145
|
+
id: "a3",
|
|
146
|
+
filename: "homepage-hero.png",
|
|
147
|
+
mimeType: "image/png",
|
|
148
|
+
size: 1200000,
|
|
149
|
+
url: "#",
|
|
150
|
+
thumbnailUrl:
|
|
151
|
+
"https://placehold.co/400x300/e2e8f0/475569?text=Homepage+Hero",
|
|
152
|
+
inline: false,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: "a4",
|
|
156
|
+
filename: "homepage-features.png",
|
|
157
|
+
mimeType: "image/png",
|
|
158
|
+
size: 980000,
|
|
159
|
+
url: "#",
|
|
160
|
+
thumbnailUrl:
|
|
161
|
+
"https://placehold.co/400x300/e2e8f0/475569?text=Features+Section",
|
|
162
|
+
inline: false,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
id: "a5",
|
|
166
|
+
filename: "homepage-pricing.png",
|
|
167
|
+
mimeType: "image/png",
|
|
168
|
+
size: 850000,
|
|
169
|
+
url: "#",
|
|
170
|
+
thumbnailUrl:
|
|
171
|
+
"https://placehold.co/400x300/e2e8f0/475569?text=Pricing+Table",
|
|
172
|
+
inline: false,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
labels: [{ id: "l4", name: "Design", color: "#10b981", type: "user" }],
|
|
176
|
+
isRead: true,
|
|
177
|
+
isStarred: false,
|
|
178
|
+
isImportant: false,
|
|
179
|
+
isDraft: false,
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const messageWithReplyTo: Message = {
|
|
183
|
+
id: "m5",
|
|
184
|
+
threadId: "t5",
|
|
185
|
+
subject: "Newsletter: Weekly Product Updates",
|
|
186
|
+
from: { address: "newsletter@product.io", name: "Product Updates" },
|
|
187
|
+
to: [{ address: "me@company.com", name: "Me" }],
|
|
188
|
+
replyTo: { address: "support@product.io", name: "Product Support" },
|
|
189
|
+
date: new Date(Date.now() - 86400000).toISOString(),
|
|
190
|
+
snippet: "Check out what's new this week...",
|
|
191
|
+
htmlBody: `<div>
|
|
192
|
+
<h2 style="color: #3b82f6;">What's New This Week</h2>
|
|
193
|
+
<ul>
|
|
194
|
+
<li>New dashboard widgets</li>
|
|
195
|
+
<li>Improved export functionality</li>
|
|
196
|
+
<li>Bug fixes and performance improvements</li>
|
|
197
|
+
</ul>
|
|
198
|
+
<p>Thanks for using our product!</p>
|
|
199
|
+
</div>`,
|
|
200
|
+
attachments: [],
|
|
201
|
+
labels: [],
|
|
202
|
+
isRead: true,
|
|
203
|
+
isStarred: false,
|
|
204
|
+
isImportant: false,
|
|
205
|
+
isDraft: false,
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const unreadMessage: Message = {
|
|
209
|
+
id: "m6",
|
|
210
|
+
threadId: "t6",
|
|
211
|
+
subject: "Meeting Notes - Sprint Planning",
|
|
212
|
+
from: { address: "john@company.com", name: "John Doe" },
|
|
213
|
+
to: [
|
|
214
|
+
{ address: "me@company.com", name: "Me" },
|
|
215
|
+
{ address: "team@company.com", name: "Development Team" },
|
|
216
|
+
],
|
|
217
|
+
date: new Date(Date.now() - 900000).toISOString(),
|
|
218
|
+
snippet: "Here are the notes from today's sprint planning meeting.",
|
|
219
|
+
textBody: `Team,
|
|
220
|
+
|
|
221
|
+
Here are the notes from today's sprint planning meeting:
|
|
222
|
+
|
|
223
|
+
Sprint Goals:
|
|
224
|
+
- Complete user authentication flow
|
|
225
|
+
- Implement dashboard analytics
|
|
226
|
+
- Fix critical bugs from last sprint
|
|
227
|
+
|
|
228
|
+
Action Items:
|
|
229
|
+
- @sarah: Set up OAuth integration
|
|
230
|
+
- @mike: Design dashboard mockups
|
|
231
|
+
- @alex: Review and prioritize bug backlog
|
|
232
|
+
|
|
233
|
+
Next meeting: Friday at 2pm
|
|
234
|
+
|
|
235
|
+
Cheers,
|
|
236
|
+
John`,
|
|
237
|
+
attachments: [
|
|
238
|
+
{
|
|
239
|
+
id: "a6",
|
|
240
|
+
filename: "sprint-planning-notes.docx",
|
|
241
|
+
mimeType:
|
|
242
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
243
|
+
size: 45000,
|
|
244
|
+
url: "#",
|
|
245
|
+
inline: false,
|
|
246
|
+
},
|
|
247
|
+
],
|
|
248
|
+
labels: [{ id: "l5", name: "Meetings", color: "#8b5cf6", type: "user" }],
|
|
249
|
+
isRead: false,
|
|
250
|
+
isStarred: false,
|
|
251
|
+
isImportant: false,
|
|
252
|
+
isDraft: false,
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
const meta: Meta<typeof MessageView> = {
|
|
256
|
+
title: "Zero/Mail/MessageView",
|
|
257
|
+
component: MessageView,
|
|
258
|
+
parameters: {
|
|
259
|
+
layout: "centered",
|
|
260
|
+
},
|
|
261
|
+
tags: ["autodocs"],
|
|
262
|
+
argTypes: {
|
|
263
|
+
isExpanded: {
|
|
264
|
+
control: "boolean",
|
|
265
|
+
},
|
|
266
|
+
showHeaders: {
|
|
267
|
+
control: "boolean",
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
decorators: [
|
|
271
|
+
(Story) => (
|
|
272
|
+
<div className="w-full max-w-4xl bg-background">
|
|
273
|
+
<Story />
|
|
274
|
+
</div>
|
|
275
|
+
),
|
|
276
|
+
],
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export default meta;
|
|
280
|
+
type Story = StoryObj<typeof meta>;
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Default expanded message view.
|
|
284
|
+
* Shows full message content with action buttons.
|
|
285
|
+
*/
|
|
286
|
+
export const Default: Story = {
|
|
287
|
+
args: {
|
|
288
|
+
message: simpleMessage,
|
|
289
|
+
isExpanded: true,
|
|
290
|
+
showHeaders: false,
|
|
291
|
+
showReplyComposer: false,
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Message with rich HTML content and formatting.
|
|
297
|
+
* Includes lists, bold text, and structured content.
|
|
298
|
+
*/
|
|
299
|
+
export const RichHtml: Story = {
|
|
300
|
+
args: {
|
|
301
|
+
message: richHtmlMessage,
|
|
302
|
+
isExpanded: true,
|
|
303
|
+
showHeaders: false,
|
|
304
|
+
showReplyComposer: false,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Starred and important message.
|
|
310
|
+
* Shows star icon in header and importance indicators.
|
|
311
|
+
*/
|
|
312
|
+
export const Starred: Story = {
|
|
313
|
+
args: {
|
|
314
|
+
message: starredMessage,
|
|
315
|
+
isExpanded: true,
|
|
316
|
+
showHeaders: false,
|
|
317
|
+
showReplyComposer: false,
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Collapsed message view.
|
|
323
|
+
* Shows only snippet and sender avatar.
|
|
324
|
+
*/
|
|
325
|
+
export const Collapsed: Story = {
|
|
326
|
+
args: {
|
|
327
|
+
message: simpleMessage,
|
|
328
|
+
isExpanded: false,
|
|
329
|
+
showHeaders: false,
|
|
330
|
+
showReplyComposer: false,
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Message with CC recipients.
|
|
336
|
+
* Shows additional recipient fields when headers expanded.
|
|
337
|
+
*/
|
|
338
|
+
export const WithCc: Story = {
|
|
339
|
+
args: {
|
|
340
|
+
message: richHtmlMessage,
|
|
341
|
+
isExpanded: true,
|
|
342
|
+
showHeaders: true,
|
|
343
|
+
showReplyComposer: false,
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Message with Reply-To address.
|
|
349
|
+
* Shows Reply-To field in expanded headers.
|
|
350
|
+
*/
|
|
351
|
+
export const WithReplyTo: Story = {
|
|
352
|
+
args: {
|
|
353
|
+
message: messageWithReplyTo,
|
|
354
|
+
isExpanded: true,
|
|
355
|
+
showHeaders: true,
|
|
356
|
+
showReplyComposer: false,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Message with file attachments.
|
|
362
|
+
* Shows attachment list below message body.
|
|
363
|
+
*/
|
|
364
|
+
export const WithAttachments: Story = {
|
|
365
|
+
args: {
|
|
366
|
+
message: richHtmlMessage,
|
|
367
|
+
isExpanded: true,
|
|
368
|
+
showHeaders: false,
|
|
369
|
+
showReplyComposer: false,
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Message with image attachments.
|
|
375
|
+
* Shows image thumbnails in attachment list.
|
|
376
|
+
*/
|
|
377
|
+
export const WithImages: Story = {
|
|
378
|
+
args: {
|
|
379
|
+
message: messageWithImages,
|
|
380
|
+
isExpanded: true,
|
|
381
|
+
showHeaders: false,
|
|
382
|
+
showReplyComposer: false,
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Unread message.
|
|
388
|
+
* Typically shown with different styling in thread context.
|
|
389
|
+
*/
|
|
390
|
+
export const Unread: Story = {
|
|
391
|
+
args: {
|
|
392
|
+
message: unreadMessage,
|
|
393
|
+
isExpanded: true,
|
|
394
|
+
showHeaders: false,
|
|
395
|
+
showReplyComposer: false,
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Message with multiple labels.
|
|
401
|
+
* Shows colored badges for message categorization.
|
|
402
|
+
*/
|
|
403
|
+
export const WithLabels: Story = {
|
|
404
|
+
args: {
|
|
405
|
+
message: starredMessage,
|
|
406
|
+
isExpanded: true,
|
|
407
|
+
showHeaders: false,
|
|
408
|
+
showReplyComposer: false,
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Plain text message without HTML.
|
|
414
|
+
* Preserves formatting with whitespace.
|
|
415
|
+
*/
|
|
416
|
+
export const PlainText: Story = {
|
|
417
|
+
args: {
|
|
418
|
+
message: simpleMessage,
|
|
419
|
+
isExpanded: true,
|
|
420
|
+
showHeaders: false,
|
|
421
|
+
showReplyComposer: false,
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Message header component in expanded state.
|
|
427
|
+
* Shows all recipient details and metadata.
|
|
428
|
+
*/
|
|
429
|
+
export const HeaderExpanded: StoryObj<typeof MessageHeader> = {
|
|
430
|
+
render: () => (
|
|
431
|
+
<div className="max-w-4xl border rounded-lg bg-background p-4">
|
|
432
|
+
<MessageHeader message={richHtmlMessage} expanded={true} />
|
|
433
|
+
</div>
|
|
434
|
+
),
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Message header component in collapsed state.
|
|
439
|
+
* Shows minimal recipient information.
|
|
440
|
+
*/
|
|
441
|
+
export const HeaderCollapsed: StoryObj<typeof MessageHeader> = {
|
|
442
|
+
render: () => (
|
|
443
|
+
<div className="max-w-4xl border rounded-lg bg-background p-4">
|
|
444
|
+
<MessageHeader message={richHtmlMessage} expanded={false} />
|
|
445
|
+
</div>
|
|
446
|
+
),
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Message header with star indicator.
|
|
451
|
+
* Shows visual indicator for starred messages.
|
|
452
|
+
*/
|
|
453
|
+
export const HeaderStarred: StoryObj<typeof MessageHeader> = {
|
|
454
|
+
render: () => (
|
|
455
|
+
<div className="max-w-4xl border rounded-lg bg-background p-4">
|
|
456
|
+
<MessageHeader message={starredMessage} expanded={false} />
|
|
457
|
+
</div>
|
|
458
|
+
),
|
|
459
|
+
};
|