diff --git a/frontend/src/admin/EmailTemplateEditor.tsx b/frontend/src/admin/EmailTemplateEditor.tsx index 8b25cda..eb711aa 100644 --- a/frontend/src/admin/EmailTemplateEditor.tsx +++ b/frontend/src/admin/EmailTemplateEditor.tsx @@ -8,6 +8,10 @@ import Col from 'antd/es/col'; import Alert from 'antd/es/alert'; import Typography from 'antd/es/typography'; import message from 'antd/es/message'; +import MDEditor from '@uiw/react-md-editor'; +import '@uiw/react-md-editor/markdown-editor.css'; +import '@uiw/react-markdown-preview/markdown.css'; +import { useThemeMode } from '../theme/ThemeContext'; import { EmailTemplate, saveEmailTemplate, @@ -27,6 +31,7 @@ type Props = Readonly<{ const PREVIEW_DEBOUNCE_MS = 400; export default function EmailTemplateEditor({ template, onChanged }: Props) { + const { mode } = useThemeMode(); // Falls back to the default so the editor starts from the real copy rather // than an empty box. `subject`/`body` being null means "never customised", // which is why the badge on the tab can say so. @@ -111,13 +116,28 @@ export default function EmailTemplateEditor({ template, onChanged }: Props) { placeholder="Subject" style={{ marginBottom: 8 }} /> - setBody(e.target.value)} - autoSize={{ minRows: 10, maxRows: 24 }} - style={{ fontFamily: 'monospace', marginBottom: 12 }} - /> + {/* The same editor the item description uses, so an admin who has + learned one toolbar has learned both. data-color-mode is what makes + it follow the admin's dark mode, exactly as on the item form. + + preview="edit" rather than the item form's "live": MDEditor's own + preview would render the markdown with a different renderer, showing + a literal {{resetUrl}} and no consent footer. The pane on the right + is the server's rendering of the actual email, and two previews + disagreeing about the thing being edited is worse than one. */} +
+ setBody(val || '')} + height={320} + preview="edit" + // Input.TextArea carried this attribute itself. MDEditor owns the + // textarea, so the label has to be passed through — without it every + // email template test loses its handle on the field, and a screen + // reader loses the only thing naming it. + textareaProps={{ 'aria-label': `${template.label} body` }} + /> +