Skip to content

Commit 9d6780f

Browse files
committed
feat: add support for custom HTML and text templates in MailNotification
1 parent 96ff024 commit 9d6780f

2 files changed

Lines changed: 103 additions & 16 deletions

File tree

packages/notifications/src/drivers/MailNotification.ts

Lines changed: 66 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,9 @@ export class MailNotification extends NotificationContract {
1212
private fromAddress?: string
1313
private subjectLine?: string
1414
private recipients?: MailRecipient
15-
static defaultHtmlTemplate = [
16-
'<!doctype html>',
17-
'<html>',
18-
'<body style="font-family:Arial,sans-serif;line-height:1.5;color:#111827">',
19-
'<h2>{subject}</h2>',
20-
'<div>{message}</div>',
21-
'<p style="color:#6b7280;font-size:12px">&copy; {year} {app_name}</p>',
22-
'</body>',
23-
'</html>',
24-
].join('')
15+
private ViewName: string = '~arkstack/notifications.mail'
16+
private htmlTemplate?: string
17+
private textTemplate?: string
2518

2619
constructor(options: MailDriverOptions = {}) {
2720
super()
@@ -57,18 +50,66 @@ export class MailNotification extends NotificationContract {
5750
return this
5851
}
5952

53+
/**
54+
* The email subject
55+
*
56+
* @param subject
57+
* @returns
58+
*/
6059
subject (subject: string): this {
6160
this.subjectLine = subject
6261

6362
return this
6463
}
6564

65+
/**
66+
* Set email the notification recipeint
67+
*
68+
* @param recipient string or array of email addresses
69+
* @returns
70+
*/
6671
recipient (recipient: MailRecipient): this {
6772
this.recipients = recipient
6873

6974
return this
7075
}
7176

77+
/**
78+
* Set email the notification view name
79+
*
80+
* @param view view name
81+
* @returns
82+
*/
83+
view (view: string): this {
84+
this.ViewName = view
85+
86+
return this
87+
}
88+
89+
/**
90+
* Set email the notification html template
91+
*
92+
* @param content view name
93+
* @returns
94+
*/
95+
html (content: string): this {
96+
this.htmlTemplate = content
97+
98+
return this
99+
}
100+
101+
/**
102+
* Set email the notification text template
103+
*
104+
* @param content view name
105+
* @returns
106+
*/
107+
text (content: string): this {
108+
this.textTemplate = content
109+
110+
return this
111+
}
112+
72113
/**
73114
* Prepare a notification to be sent.
74115
*
@@ -115,16 +156,25 @@ export class MailNotification extends NotificationContract {
115156
throw new Error('No recipient provided for mail notification')
116157
}
117158

159+
const templateData = {
160+
...mergedData,
161+
message: resolvedMessage,
162+
subject: resolvedSubject,
163+
}
164+
165+
const textMessage = resolvedMessage.replace(/<\/?[^>]+(>|$)/g, '')
166+
118167
return await this.driver.sendMail({
119168
to: to as never,
120169
subject: resolvedSubject,
121170
from: this.fromAddress,
122-
text: resolvedMessage.replace(/<\/?[^>]+(>|$)/g, ''),
123-
html: interpolate(await globalThis.view('~arkstack/notifications.mail', {
124-
...mergedData,
125-
message: resolvedMessage,
126-
subject: resolvedSubject,
127-
})),
171+
text: interpolate(
172+
this.textTemplate ?? textMessage,
173+
{ ...templateData, message: textMessage }
174+
),
175+
html: this.htmlTemplate
176+
? interpolate(this.htmlTemplate, templateData)
177+
: await globalThis.view(this.ViewName, templateData),
128178
})
129179
}
130180

packages/notifications/tests/notification.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,43 @@ describe('Notification', () => {
177177
expect(content).toContain('<div><strong>Hello Ada</strong></div>')
178178
})
179179

180+
it('delivers mail with custom html and text templates', async () => {
181+
const response = await Notification.mail({
182+
from: 'noreply@arkstack.test',
183+
host: 'smtp.arkstack.test',
184+
port: 2525,
185+
})
186+
.recipient(['ada@example.com', 'grace@example.com'])
187+
.subject('Welcome, {name}')
188+
.data({
189+
app_name: 'Arkstack',
190+
name: 'Ada',
191+
})
192+
.text('Subject: {subject}\nMessage: {message}')
193+
.html(`
194+
<html>
195+
<head><title>{subject}</title></head>
196+
<body>{message}</body>
197+
</html>
198+
`)
199+
.send('<strong>Hello {name}</strong>')
200+
201+
expect(response).toEqual({
202+
accepted: ['ada@example.com', 'grace@example.com'],
203+
messageId: 'test-message-id',
204+
})
205+
expect(mocks.sendMail).toHaveBeenCalledTimes(1)
206+
expect(mocks.sendMail).toHaveBeenCalledWith(expect.objectContaining({
207+
from: 'noreply@arkstack.test',
208+
subject: 'Welcome, Ada',
209+
text: 'Subject: Welcome, Ada\nMessage: Hello Ada',
210+
to: ['ada@example.com', 'grace@example.com'],
211+
}))
212+
const content = String(mocks.sendMail.mock.calls[0][0].html)
213+
expect(content).toContain('<title>Welcome, Ada</title>')
214+
expect(content).toContain('<body><strong>Hello Ada</strong></body>')
215+
})
216+
180217
it('delivers mail to named address recipients', async () => {
181218
const recipients = [
182219
{ 'ada@example.com': 'Ada Lovelace' },

0 commit comments

Comments
 (0)