[EMAIL-PREVIEW-1] PLU-386: add TFieldPreviewType and previewType to IFieldRichText#1652
[EMAIL-PREVIEW-1] PLU-386: add TFieldPreviewType and previewType to IFieldRichText#1652ogp-weeloong wants to merge 3 commits into
Conversation
How to use the Graphite Merge QueueAdd the label lfg to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
Adds a narrow union type marker (`'email'` for now) plus an optional `previewType` field on rich-text fields. This will let downstream code opt a rich-text field into a kind-specific preview modal in FlowStepTestController without churning the schema for future preview kinds (e.g. SMS). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Installs @emailens/engine in the frontend workspace; the slice we use (transformForClient) only needs cheerio + css-tree which are browser-safe. The engine declares engines.node: ">=18", which is a deliberate tech-debt acceptance. To guard against future regressions where the engine — or any other dep — starts importing a node-only module, add a Rollup onwarn handler in both vite.config.ts and vite.config.lib.ts that promotes the "has been externalized for browser compatibility" warning (and MISSING_NODE_BUILTINS) into a hard build error. The comment + error message name @emailens/engine explicitly so future readers understand why this guardrail exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13c6512 to
ba98c73
Compare
Adds a self-contained, lazy-loaded modal that renders an HTML email body through @emailens/engine's transformForClient for one of four target clients (Outlook Classic, Gmail, Apple Mail, Yahoo Mail), defaulting to Outlook Classic (Word engine — worst-case rendering). - index.tsx: React.lazy wrapper + Suspense boundary so consumers don't pay the engine's bundle cost up front. - EmailPreviewModal.tsx: real implementation. Chakra Flex (fixed-width left rail of client options, flex="1" iframe on the right). The iframe uses sandbox="" (no allow-* flags) to block scripts, forms, and navigation in user HTML. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ba98c73 to
aa87e38
Compare
kevinkim-ogp
left a comment
There was a problem hiding this comment.
other than a few minor things, lgtm!
can still create and send email, can still view past emails
| ) | ||
|
|
||
| const transformed = useMemo(() => { | ||
| return transformForClient(html, selectedClientId).html |
There was a problem hiding this comment.
should this be wrapped in a try/catch: if it so happens to throw an error due to malformed HTML, the preview might break?
also do we want to only transform when the isOpen is true?
| // Guardrail for the @emailens/engine tech-debt: the engine declares | ||
| // `engines.node: ">=18"` but the slice we use (transformForClient) is | ||
| // browser-safe. If a future version of @emailens/engine — or any other | ||
| // dep — starts importing a node-only module (fs, path, crypto, etc.), | ||
| // Vite externalizes it and emits a warning. We promote that warning to | ||
| // a hard error so CI catches the regression instead of silently | ||
| // shipping a broken bundle. | ||
| onwarn(warning, defaultHandler) { | ||
| const msg = warning.message ?? '' | ||
| if ( | ||
| msg.includes('has been externalized for browser compatibility') || | ||
| warning.code === 'MISSING_NODE_BUILTINS' | ||
| ) { | ||
| throw new Error( | ||
| `[vite-guardrail] A node-only import leaked into the frontend bundle. ` + | ||
| `This guardrail exists to catch @emailens/engine (and similar libraries) ` + | ||
| `pulling in backend-only modules. Original warning: ${msg}`, | ||
| ) | ||
| } | ||
| defaultHandler(warning) | ||
| }, |
There was a problem hiding this comment.
nit: do we want to extract this to a shared helper since its the same thing in both vite.config.lib.ts and vite.config.ts ?

Problem
We want pipe owners to be able to preview their email across a variety of email clients (Outlook, Yahoo etc).
High-level Approach
We will use the emaillens library to generate the preview. This is a front-end only change; high level approach is
previewTypeproperty in theIRichTextFieldtype.emailpreview type.IMPORTANT
The
emailenslibrary is technically meant to be run on a node backend. However, I want this to run on the frontend to reduce latency and save costs.It turns out you can run this on the browser as long as we don't need the features [see github] that use node stuff:
@emailens/engine/server— importsdns(thecheckDeliverability+ SpamAssassin stuff).@emailens/engine/compile— JSX/MJML/Maizzle compilation, which usesnode:vm/isolated-vm/mjmletc.I added a vite safeguard to prevent us from from doing so. In the event that emailens updates the code to make this impossible, we can always add a REST API to do this (non-ideal)
This PR
previewTypefieldemailvalue for preview type and also a newPreviewEmailModalfor previewing emails in selected clientsTests
Only regression tests as this is a no-op.
Full test in later PR