Skip to content

🎨 Added Preview tab to welcome email editor#27399

Merged
cmraible merged 14 commits into
mainfrom
chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email
Apr 22, 2026
Merged

🎨 Added Preview tab to welcome email editor#27399
cmraible merged 14 commits into
mainfrom
chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email

Conversation

@cmraible

@cmraible cmraible commented Apr 14, 2026

Copy link
Copy Markdown
Collaborator

ref https://linear.app/ghost/issue/NY-1228/add-the-frontend-editpreview-toggle-to-the-welcome-email-edit-modal

What this changes

  • adds an Edit / Preview toggle to the welcome email modal in Admin
  • lets admins preview unsaved subject and body changes before saving
  • loads the preview from the automated email preview endpoint and renders it in an iframe sized to the email content
  • preserves unsaved draft state when switching between edit and preview
  • shows inline validation and preview loading/error states in the modal
  • ignores stale out-of-order preview responses so older requests cannot overwrite a newer draft

Why

The welcome email editor previously only exposed the raw editing experience, which made it harder to verify the final email output before saving. This change adds an in-context preview flow so admins can check both copy and rendered layout while keeping their draft intact.

Implementation notes

  • adds a dedicated usePreviewWelcomeEmail mutation and preview response types in admin-x-framework
  • updates the modal header layout to host the segmented mode switch
  • normalizes preview HTML links to open safely in a new tab
  • measures iframe height so the modal owns scrolling instead of the preview document
  • extends acceptance and browser E2E coverage for preview rendering and draft preservation

@coderabbitai

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds end-to-end support for automated welcome-email previews: new API types and a POST preview mutation hook; a useWelcomeEmailPreview hook that validates drafts, handles concurrency, processes HTML, and maps errors; a WelcomeEmailPreviewFrame component that renders and height-syncs preview HTML in an iframe; welcome-email validation utilities; modal UI updated with edit/preview modes and subject override; Koenig editor typography adjustments; Playwright page helper locators for preview mode; and new/updated unit, acceptance, and e2e tests covering preview behaviors.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Added Preview tab to welcome email editor' accurately describes the main feature added in this PR, which is the new Edit/Preview toggle UI component for the welcome email modal.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining what was changed (Preview/Edit toggle), why it matters, and how it was implemented across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cmraible cmraible force-pushed the chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email branch 2 times, most recently from b67218c to 2fc597d Compare April 14, 2026 23:02
Base automatically changed from chris-ny-1227-add-the-backend-api-endpoint-to-preview-a-welcome-email to main April 15, 2026 19:23
@cmraible cmraible changed the title 🎨 Added welcome email edit/preview toggle 🎨 Added Preview tab to welcome email editor Apr 15, 2026
@cmraible cmraible force-pushed the chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email branch 2 times, most recently from 0a0517c to 137dc14 Compare April 15, 2026 22:37
@cmraible cmraible added the preview Deploy a PR preview environment label Apr 15, 2026
@Ghost-Slimer Ghost-Slimer temporarily deployed to pr-preview-27399 April 15, 2026 23:01 Destroyed
@cmraible cmraible force-pushed the chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email branch from 137dc14 to 6654797 Compare April 15, 2026 23:08
@cmraible cmraible requested a review from troyciesco April 15, 2026 23:34
@github-actions

Copy link
Copy Markdown
Contributor

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 24520623759 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

Comment thread apps/admin-x-settings/src/hooks/use-welcome-email-preview.ts Outdated
Comment thread apps/admin-x-settings/src/hooks/use-welcome-email-preview.ts Outdated
@cmraible cmraible force-pushed the chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email branch from 3c4a7d2 to b7034f5 Compare April 21, 2026 17:42
@cmraible cmraible marked this pull request as ready for review April 21, 2026 17:47
@cmraible cmraible requested a review from 9larsons as a code owner April 21, 2026 17:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
e2e/helpers/pages/admin/settings/sections/member-welcome-emails-section.ts (1)

89-90: Aliased modalSubjectInput only works in preview mode.

modalSubjectInput now points at the preview-only welcome-email-preview-subject input (which is unmounted in edit mode). Any existing or future callers that interact with modalSubjectInput without first switching to preview mode will hit a hidden locator. Consider either removing the alias and forcing callers to use modalPreviewSubjectInput explicitly (clearer intent), or documenting the mode requirement on this field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/helpers/pages/admin/settings/sections/member-welcome-emails-section.ts`
around lines 89 - 90, The aliasing of modalSubjectInput to the preview-only
locator welcome-email-preview-subject causes hidden-locator errors in edit mode;
change modalSubjectInput so it does not point at the preview-only element —
either remove the alias and require callers to use modalPreviewSubjectInput
explicitly, or make modalSubjectInput switch its underlying locator based on
mode (preview vs edit) by selecting the preview locator
(welcome-email-preview-subject) only when preview mode is active; update any
callers of modalSubjectInput to use modalPreviewSubjectInput or to ensure the
modal is in preview mode before interacting.
apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx (1)

105-105: previewSubjectOverride appears redundant.

On every keystroke in the preview subject field (Line 296-300), both previewSubjectOverride and formState.subject are set to the same value, and on mode transitions the override is reset to null (Lines 181, 185). Since the two values are always in sync after any edit and the displayed value is previewSubjectOverride ?? formState.subject, the override never changes the rendered subject. Consider removing the extra state and reading/writing formState.subject directly — fewer moving parts and no risk of the two drifting.

Proposed simplification
-    const [previewSubjectOverride, setPreviewSubjectOverride] = useState<string | null>(null);
@@
-    const handleModeChange = useCallback((nextMode: PreviewMode) => {
-        setMode(nextMode);
-
-        if (nextMode === 'preview') {
-            setPreviewSubjectOverride(null);
-            enterPreview(formState);
-        } else {
-            setShowTestDropdown(false);
-            setPreviewSubjectOverride(null);
-            exitPreview();
-        }
-    }, [enterPreview, exitPreview, formState]);
+    const handleModeChange = useCallback((nextMode: PreviewMode) => {
+        setMode(nextMode);
+
+        if (nextMode === 'preview') {
+            enterPreview(formState);
+        } else {
+            setShowTestDropdown(false);
+            exitPreview();
+        }
+    }, [enterPreview, exitPreview, formState]);
@@
-                                            value={previewSubjectOverride ?? formState.subject}
-                                            onChange={(e) => {
-                                                const nextSubject = e.target.value;
-                                                setPreviewSubjectOverride(nextSubject);
-                                                updateForm(state => ({...state, subject: nextSubject}));
-                                            }}
+                                            value={formState.subject}
+                                            onChange={e => updateForm(state => ({...state, subject: e.target.value}))}

Also applies to: 177-188, 292-301

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx`
at line 105, Remove the redundant previewSubjectOverride state and its setter
(previewSubjectOverride, setPreviewSubjectOverride) and use formState.subject
everywhere instead: update the subject input change handler (currently setting
both previewSubjectOverride and formState.subject) to only update
formState.subject, remove any code that resets previewSubjectOverride on mode
transitions, and change the displayed subject expression from
previewSubjectOverride ?? formState.subject to just formState.subject; ensure
any references to setPreviewSubjectOverride are deleted and tests/handlers that
relied on the override now read/write formState.subject directly.
apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts (1)

284-285: Replace deprecated Locator.type() with pressSequentially().

locator.type() is deprecated in Playwright 1.59; use pressSequentially() for character-by-character input instead.

Proposed change
-            await previewSubject.press('End');
-            await previewSubject.type('!');
+            await previewSubject.press('End');
+            await previewSubject.pressSequentially('!');
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts`
around lines 284 - 285, Replace the deprecated locator.type call with
pressSequentially: locate the previewSubject.type('!') call and change it to use
previewSubject.pressSequentially with an array of character strings (e.g. ['!'])
so the input is sent character-by-character; keep the existing
previewSubject.press('End') as-is and await the new pressSequentially promise to
ensure ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@e2e/tests/admin/settings/member-welcome-emails.test.ts`:
- Around line 188-199: The test currently waits for the preview POST
(previewResponse) before changing the subject, so subject edits may not be
included; after clicking modalPreviewTab and before asserting the preview,
trigger and await the request caused by the subject edit: fill modalSubjectInput
with customSubject and create a new waitForResponse that looks for a POST to
'/ghost/api/admin/automated_emails/' containing '/preview/' whose
response/request body includes customSubject (or assert the returned
JSON/response text contains customSubject), then await that response and finally
assert modalPreviewSubjectInput has customSubject; use the existing symbols
previewResponse/modalPreviewTab/modalSubjectInput/modalPreviewSubjectInput/customSubject
to locate and implement this change.
- Line 179: Rename the test titles passed to test(...) in
e2e/tests/admin/settings/member-welcome-emails.test.ts to follow the lowercase
"what is tested - expected outcome" convention; specifically change the string
"free welcome email preview renders edited subject and body" to "free welcome
email preview - renders edited subject and body" (and apply the same pattern to
the other test around the same block referenced in the review) so all test
descriptions use the required lowercase hyphenated format.
- Line 201: The test is using a CSS body selector via
welcomeEmailsSection.modalPreviewFrame.locator('body'); remove the CSS selector
and assert the text directly on the frame locator instead: stop calling
locator('body') and call the frame locator
(welcomeEmailsSection.modalPreviewFrame) in the expect assertion to verify it
contains the customBody text (use the frame locator's semantic text assertion,
e.g., the toContainText-style assertion).
- Around line 210-254: The test currently mutates the welcome email and only
restores the subject; capture the original welcome email object before any
modifications (call getFreeWelcomeEmail(page) into e.g.
originalFreeWelcomeEmail) and in the finally block call
restoreWelcomeEmail(page, originalFreeWelcomeEmail) to restore the entire record
(subject, body, status, etc.) instead of only resetting the subject to
DEFAULT_FREE_WELCOME_EMAIL_SUBJECT; locate usages around welcomeEmailsSection,
getFreeWelcomeEmail, and restoreWelcomeEmail to implement this change.

---

Nitpick comments:
In
`@apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx`:
- Line 105: Remove the redundant previewSubjectOverride state and its setter
(previewSubjectOverride, setPreviewSubjectOverride) and use formState.subject
everywhere instead: update the subject input change handler (currently setting
both previewSubjectOverride and formState.subject) to only update
formState.subject, remove any code that resets previewSubjectOverride on mode
transitions, and change the displayed subject expression from
previewSubjectOverride ?? formState.subject to just formState.subject; ensure
any references to setPreviewSubjectOverride are deleted and tests/handlers that
relied on the override now read/write formState.subject directly.

In
`@apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts`:
- Around line 284-285: Replace the deprecated locator.type call with
pressSequentially: locate the previewSubject.type('!') call and change it to use
previewSubject.pressSequentially with an array of character strings (e.g. ['!'])
so the input is sent character-by-character; keep the existing
previewSubject.press('End') as-is and await the new pressSequentially promise to
ensure ordering.

In `@e2e/helpers/pages/admin/settings/sections/member-welcome-emails-section.ts`:
- Around line 89-90: The aliasing of modalSubjectInput to the preview-only
locator welcome-email-preview-subject causes hidden-locator errors in edit mode;
change modalSubjectInput so it does not point at the preview-only element —
either remove the alias and require callers to use modalPreviewSubjectInput
explicitly, or make modalSubjectInput switch its underlying locator based on
mode (preview vs edit) by selecting the preview locator
(welcome-email-preview-subject) only when preview mode is active; update any
callers of modalSubjectInput to use modalPreviewSubjectInput or to ensure the
modal is in preview mode before interacting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 18fa7537-a16e-4dad-80d9-cf154c08df63

📥 Commits

Reviewing files that changed from the base of the PR and between 99e691d and b7034f5.

📒 Files selected for processing (10)
  • apps/admin-x-framework/src/api/automated-emails.ts
  • apps/admin-x-settings/src/components/settings/membership/member-emails/member-email-editor.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/use-welcome-email-preview.ts
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-preview-frame.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-validation.ts
  • apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts
  • apps/admin-x-settings/test/unit/hooks/use-welcome-email-preview.test.tsx
  • e2e/helpers/pages/admin/settings/sections/member-welcome-emails-section.ts
  • e2e/tests/admin/settings/member-welcome-emails.test.ts

Comment thread e2e/tests/admin/settings/member-welcome-emails.test.ts Outdated
Comment thread e2e/tests/admin/settings/member-welcome-emails.test.ts
Comment thread e2e/tests/admin/settings/member-welcome-emails.test.ts Outdated
Comment thread e2e/tests/admin/settings/member-welcome-emails.test.ts
@cmraible cmraible requested a review from troyciesco April 21, 2026 18:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
e2e/tests/admin/settings/member-welcome-emails.test.ts (1)

225-226: Replace deprecated type() with fill().

Locator.type() is deprecated in Playwright and should be replaced with fill() for form-filling scenarios. This is faster and directly triggers input events. Since you're setting the final value of the input, fill() is appropriate here.

♻️ Suggested change
-            await welcomeEmailsSection.modalSubjectInput.press('End');
-            await welcomeEmailsSection.modalSubjectInput.type('!');
+            await welcomeEmailsSection.modalSubjectInput.fill(templatedSubjectWithSuffix);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/tests/admin/settings/member-welcome-emails.test.ts` around lines 225 -
226, The test currently uses the deprecated Locator.type() on
welcomeEmailsSection.modalSubjectInput to append a '!' after pressing End;
replace this with using inputValue() to read the current value and then fill()
with the new value (e.g., read
welcomeEmailsSection.modalSubjectInput.inputValue(), concatenate '!' and call
welcomeEmailsSection.modalSubjectInput.fill(newValue)) so the final input is set
via fill() instead of the deprecated type().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@e2e/tests/admin/settings/member-welcome-emails.test.ts`:
- Around line 225-226: The test currently uses the deprecated Locator.type() on
welcomeEmailsSection.modalSubjectInput to append a '!' after pressing End;
replace this with using inputValue() to read the current value and then fill()
with the new value (e.g., read
welcomeEmailsSection.modalSubjectInput.inputValue(), concatenate '!' and call
welcomeEmailsSection.modalSubjectInput.fill(newValue)) so the final input is set
via fill() instead of the deprecated type().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 476631b3-8ffd-4285-a126-1603831e534c

📥 Commits

Reviewing files that changed from the base of the PR and between b7034f5 and e20f0a0.

📒 Files selected for processing (1)
  • e2e/tests/admin/settings/member-welcome-emails.test.ts

@github-actions

Copy link
Copy Markdown
Contributor

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 24740401196 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

1 similar comment
@github-actions

Copy link
Copy Markdown
Contributor

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 24740401196 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

This lets staff switch between editing and previewing welcome emails without
saving first, keeps the preview synced with unsaved changes, and adds focused
coverage for preview rendering, stale-response handling, and browser behavior.
peterzimon and others added 12 commits April 21, 2026 18:11
This aligns edit and preview behavior with the new workflow so content editing, preview framing, and tab affordances are consistent.
ref https://linear.app/ghost/issue/NY-1228/

The modal intentionally moved subject, sender, and test controls into
preview mode, so the acceptance and E2E coverage needed to follow the new
interaction flow and explicitly enable delivery before asserting sent
welcome emails.
ref https://linear.app/ghost/issue/NY-1228/

The preview frame was using two effects to reset state and register the same cleanup. Consolidating them keeps the lifecycle logic in one place and makes the component easier to follow during review.
The preview hook and its validation helper are only used by the welcome email modal, so keeping them under src/hooks made the feature harder to follow and forced the validation helper to be exported from the hook file just to share it with the modal.
The preview hook did not need stable callback identities for its internal control flow, and the extra wrapper around entering preview only added indirection. Simplifying these functions keeps the hook easier to read without changing behavior.
Sonar flagged the nested ternary used to derive previewFrameState, and rewriting it as a small local branch makes the returned state easier to scan without changing behavior.
Sonar flagged the legacy assert import, and the test still referenced the hook's pre-move path. Updating both imports keeps the test aligned with current Node import conventions and the colocated hook location.
The preview frame still referenced the old top-level hook path after the preview logic was moved into the member-emails feature folder. Updating the type import fixes the admin-x-settings TypeScript build without changing runtime behavior.
ref https://linear.app/tryghost/issue/NY-1228/

This locks down the preview-edit-save flow in a real browser and restores the default subject after the test so later welcome email specs stay isolated.
ref https://linear.app/tryghost/issue/NY-1228/

This aligns the preview tests with the local E2E naming rules and removes the non-semantic iframe body selector that CodeRabbit flagged.
ref https://linear.app/tryghost/issue/NY-1228/

This removes memoization that was only there to satisfy hook dependency management and keeps the preview frame logic easier to follow without changing behavior.
@cmraible cmraible force-pushed the chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email branch from 2413356 to 229c83b Compare April 22, 2026 01:11

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx`:
- Around line 290-301: The preview subject field (TextField using
value={previewSubjectOverride ?? formState.subject} and onChange that calls
setPreviewSubjectOverride/updateForm) does not show validation errors because
the modal only renders errors.lexical; update the preview subject area to also
render errors.subject using the same Hint component pattern used for lexical
errors (the Hint component near the lexical error at line 335) so that when
errors.subject exists it is displayed inline next to the preview subject field.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4fa491ab-fb1c-48fe-8275-ba82a640395e

📥 Commits

Reviewing files that changed from the base of the PR and between 2413356 and 229c83b.

📒 Files selected for processing (10)
  • apps/admin-x-framework/src/api/automated-emails.ts
  • apps/admin-x-settings/src/components/settings/membership/member-emails/member-email-editor.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/use-welcome-email-preview.ts
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-preview-frame.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-validation.ts
  • apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts
  • apps/admin-x-settings/test/unit/hooks/use-welcome-email-preview.test.tsx
  • e2e/helpers/pages/admin/settings/sections/member-welcome-emails-section.ts
  • e2e/tests/admin/settings/member-welcome-emails.test.ts
✅ Files skipped from review due to trivial changes (3)
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-validation.ts
  • apps/admin-x-settings/src/components/settings/membership/member-emails/member-email-editor.tsx
  • apps/admin-x-settings/test/acceptance/membership/member-welcome-emails.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/admin-x-framework/src/api/automated-emails.ts
  • e2e/helpers/pages/admin/settings/sections/member-welcome-emails-section.ts
  • apps/admin-x-settings/test/unit/hooks/use-welcome-email-preview.test.tsx
  • apps/admin-x-settings/src/components/settings/membership/member-emails/use-welcome-email-preview.ts

ref https://linear.app/tryghost/issue/NY-1228/

Preview mode keeps the subject field editable, so subject validation needs to stay visible there as well. This shows the existing inline error next to the preview subject input instead of only rendering lexical validation below the editor.
@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx (1)

105-105: previewSubjectOverride appears redundant with formState.subject.

The TextField's onChange sets both setPreviewSubjectOverride(nextSubject) and updateForm(state => ({...state, subject: nextSubject})) to the same value, and on every mode change previewSubjectOverride is reset to null (falling back to formState.subject). Since the subject cannot be edited anywhere else while in preview (the editor pane only receives value={formState.lexical}), the override and formState.subject can never diverge, so the override serves no user-visible purpose and just adds a second source of truth to keep in sync.

Consider dropping the override state and binding directly to formState.subject:

♻️ Proposed simplification
-    const [previewSubjectOverride, setPreviewSubjectOverride] = useState<string | null>(null);
@@
-        if (nextMode === 'preview') {
-            setPreviewSubjectOverride(null);
-            enterPreview(formState);
-        } else {
-            setShowTestDropdown(false);
-            setPreviewSubjectOverride(null);
-            exitPreview();
-        }
+        if (nextMode === 'preview') {
+            enterPreview(formState);
+        } else {
+            setShowTestDropdown(false);
+            exitPreview();
+        }
@@
-                                        <TextField
-                                            className='w-full'
-                                            data-testid='welcome-email-preview-subject'
-                                            value={previewSubjectOverride ?? formState.subject}
-                                            onChange={(e) => {
-                                                const nextSubject = e.target.value;
-                                                setPreviewSubjectOverride(nextSubject);
-                                                updateForm(state => ({...state, subject: nextSubject}));
-                                            }}
-                                        />
+                                        <TextField
+                                            className='w-full'
+                                            data-testid='welcome-email-preview-subject'
+                                            value={formState.subject}
+                                            onChange={e => updateForm(state => ({...state, subject: e.target.value}))}
+                                        />

If the intent is to later allow previewing with a subject that is not committed to the draft until save, that plan isn't realized today and the two are always kept in lockstep.

Also applies to: 295-301

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx`
at line 105, Remove the redundant previewSubjectOverride state and its setter
(previewSubjectOverride, setPreviewSubjectOverride) and instead read/write the
subject directly from formState.subject via updateForm; update the TextField
onChange to only call updateForm(state => ({...state, subject: nextSubject})),
remove any code that resets previewSubjectOverride on mode changes, and replace
any preview fallbacks that read previewSubjectOverride with formState.subject so
there is a single source of truth for the subject.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx`:
- Line 105: Remove the redundant previewSubjectOverride state and its setter
(previewSubjectOverride, setPreviewSubjectOverride) and instead read/write the
subject directly from formState.subject via updateForm; update the TextField
onChange to only call updateForm(state => ({...state, subject: nextSubject})),
remove any code that resets previewSubjectOverride on mode changes, and replace
any preview fallbacks that read previewSubjectOverride with formState.subject so
there is a single source of truth for the subject.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 46df10c7-31e5-421a-bdb2-039ed8e77b32

📥 Commits

Reviewing files that changed from the base of the PR and between 229c83b and 6f3272e.

📒 Files selected for processing (1)
  • apps/admin-x-settings/src/components/settings/membership/member-emails/welcome-email-modal.tsx

@cmraible cmraible merged commit aa41aba into main Apr 22, 2026
41 checks passed
@cmraible cmraible deleted the chris-ny-1228-add-the-frontend-editpreview-toggle-to-the-welcome-email branch April 22, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Deploy a PR preview environment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants