Skip to content

Commit 8019d6f

Browse files
feat(np): Update frontend to support new body blocks (#103001)
1 parent c38a23b commit 8019d6f

File tree

5 files changed

+140
-4
lines changed

5 files changed

+140
-4
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import {Fragment} from 'react';
2+
import styled from '@emotion/styled';
3+
4+
// Match the Python types from notifications/platform/types.py
5+
enum NotificationBodyFormattingBlockType {
6+
PARAGRAPH = 'paragraph',
7+
CODE_BLOCK = 'code_block',
8+
}
9+
10+
enum NotificationBodyTextBlockType {
11+
PLAIN_TEXT = 'plain_text',
12+
BOLD_TEXT = 'bold_text',
13+
CODE = 'code',
14+
}
15+
16+
interface NotificationBodyTextBlock {
17+
text: string;
18+
type: NotificationBodyTextBlockType;
19+
}
20+
21+
export interface NotificationBodyFormattingBlock {
22+
blocks: NotificationBodyTextBlock[];
23+
type: NotificationBodyFormattingBlockType;
24+
}
25+
26+
interface NotificationBodyRendererProps {
27+
body: NotificationBodyFormattingBlock[];
28+
codeBlockBackground?: string;
29+
codeBlockBorder?: string;
30+
codeBlockTextColor?: string;
31+
}
32+
33+
function renderTextBlock(block: NotificationBodyTextBlock, index: number) {
34+
switch (block.type) {
35+
case NotificationBodyTextBlockType.PLAIN_TEXT:
36+
return <Fragment key={index}>{block.text} </Fragment>;
37+
case NotificationBodyTextBlockType.BOLD_TEXT:
38+
return <strong key={index}>{block.text} </strong>;
39+
case NotificationBodyTextBlockType.CODE:
40+
return <code key={index}>{block.text} </code>;
41+
default:
42+
return <Fragment key={index}>{block.text} </Fragment>;
43+
}
44+
}
45+
46+
function renderFormattingBlock(
47+
block: NotificationBodyFormattingBlock,
48+
index: number,
49+
codeBlockBg: string,
50+
codeBlockBorder: string,
51+
codeBlockTextColor: string
52+
) {
53+
if (block.type === NotificationBodyFormattingBlockType.PARAGRAPH) {
54+
return (
55+
<div key={index} style={{marginBottom: '8px'}}>
56+
{block.blocks.map((textBlock, i) => renderTextBlock(textBlock, i))}
57+
</div>
58+
);
59+
}
60+
if (block.type === NotificationBodyFormattingBlockType.CODE_BLOCK) {
61+
return (
62+
<div key={index} style={{marginBottom: '8px'}}>
63+
<StyledCodeBlock
64+
backgroundColor={codeBlockBg}
65+
borderColor={codeBlockBorder}
66+
textColor={codeBlockTextColor}
67+
>
68+
{block.blocks.map((textBlock, i) => renderTextBlock(textBlock, i))}
69+
</StyledCodeBlock>
70+
</div>
71+
);
72+
}
73+
return null;
74+
}
75+
76+
const StyledCodeBlock = styled('code')<{
77+
backgroundColor: string;
78+
borderColor: string;
79+
textColor: string;
80+
}>`
81+
display: block;
82+
padding: 12px;
83+
background-color: ${p => p.backgroundColor};
84+
border: 1px solid ${p => p.borderColor};
85+
border-radius: 6px;
86+
font-family: monospace;
87+
font-size: 13px;
88+
white-space: pre-wrap;
89+
word-break: break-word;
90+
color: ${p => p.textColor};
91+
`;
92+
93+
export function NotificationBodyRenderer({
94+
body,
95+
codeBlockBackground = '#f6f8fa',
96+
codeBlockBorder = '#e1e4e8',
97+
codeBlockTextColor = '#24292e',
98+
}: NotificationBodyRendererProps) {
99+
return (
100+
<div>
101+
{body.map((block, index) =>
102+
renderFormattingBlock(
103+
block,
104+
index,
105+
codeBlockBackground,
106+
codeBlockBorder,
107+
codeBlockTextColor
108+
)
109+
)}
110+
</div>
111+
);
112+
}

static/app/debug/notifications/previews/discordPreview.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {Image} from 'sentry/components/core/image';
88
import {Container, Flex, Grid} from 'sentry/components/core/layout';
99
import {Text} from 'sentry/components/core/text';
1010
import {DebugNotificationsPreview} from 'sentry/debug/notifications/components/debugNotificationsPreview';
11+
import {NotificationBodyRenderer} from 'sentry/debug/notifications/components/notificationBodyRenderer';
1112
import {
1213
NotificationProviderKey,
1314
type NotificationTemplateRegistration,
@@ -46,7 +47,14 @@ export function DiscordPreview({
4647
<DiscordWhiteText size="md" bold>
4748
{subject}
4849
</DiscordWhiteText>
49-
<DiscordWhiteText size="sm">{JSON.stringify(body)}</DiscordWhiteText>
50+
<DiscordWhiteText size="sm">
51+
<NotificationBodyRenderer
52+
body={body}
53+
codeBlockBackground="#2f3136"
54+
codeBlockBorder="#202225"
55+
codeBlockTextColor="#dcddde"
56+
/>
57+
</DiscordWhiteText>
5058
{chart && (
5159
<DiscordChart
5260
height="100px"

static/app/debug/notifications/previews/slackPreview.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {Image} from 'sentry/components/core/image/image';
99
import {Container, Flex, Grid} from 'sentry/components/core/layout';
1010
import {Text} from 'sentry/components/core/text';
1111
import {DebugNotificationsPreview} from 'sentry/debug/notifications/components/debugNotificationsPreview';
12+
import {NotificationBodyRenderer} from 'sentry/debug/notifications/components/notificationBodyRenderer';
1213
import {
1314
NotificationProviderKey,
1415
type NotificationTemplateRegistration,
@@ -54,7 +55,13 @@ export function SlackPreview({
5455
<SlackBlackText size="xl" bold>
5556
{subject}
5657
</SlackBlackText>
57-
<SlackBodyText>{JSON.stringify(body)}</SlackBodyText>
58+
<SlackBodyText>
59+
<NotificationBodyRenderer
60+
body={body}
61+
codeBlockBackground="#f8f8f8"
62+
codeBlockBorder="#e0e0e0"
63+
/>
64+
</SlackBodyText>
5865
<Flex gap="xs">
5966
{actions.map(action => (
6067
<SlackLinkButton key={action.label} href={action.link}>

static/app/debug/notifications/previews/teamsPreview.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {Image} from 'sentry/components/core/image/image';
1111
import {Container, Flex, Grid} from 'sentry/components/core/layout';
1212
import {Text} from 'sentry/components/core/text';
1313
import {DebugNotificationsPreview} from 'sentry/debug/notifications/components/debugNotificationsPreview';
14+
import {NotificationBodyRenderer} from 'sentry/debug/notifications/components/notificationBodyRenderer';
1415
import {
1516
NotificationProviderKey,
1617
type NotificationTemplateRegistration,
@@ -75,7 +76,13 @@ export function TeamsPreview({
7576
<TeamsBlackText size="xl" bold>
7677
{subject}
7778
</TeamsBlackText>
78-
<TeamsBlackText>{JSON.stringify(body)}</TeamsBlackText>
79+
<TeamsBlackText>
80+
<NotificationBodyRenderer
81+
body={body}
82+
codeBlockBackground="#f3f2f1"
83+
codeBlockBorder="#e1dfdd"
84+
/>
85+
</TeamsBlackText>
7986
<Flex gap="md">
8087
{actions.map(action => (
8188
<TeamsLinkButton key={action.label} href={action.link}>

static/app/debug/notifications/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type {NotificationBodyFormattingBlock} from 'sentry/debug/notifications/components/notificationBodyRenderer';
2+
13
export enum NotificationProviderKey {
24
EMAIL = 'email',
35
SLACK = 'slack',
@@ -9,7 +11,7 @@ export interface NotificationTemplateRegistration {
911
category: string;
1012
example: {
1113
actions: Array<{label: string; link: string}>;
12-
body: string;
14+
body: NotificationBodyFormattingBlock[];
1315
subject: string;
1416
chart?: {alt_text: string; url: string};
1517
footer?: string;

0 commit comments

Comments
 (0)