Files
metabuilder/components/fakemui/email/inputs/BodyEditor.tsx
johndoe6345789 f22caa6e16 refactor(emailclient): full a11y pass, component decomposition, keyboard shortcuts
Accessibility:
- All components: data-testid, aria-label, aria-pressed, aria-current
- ComposeWindow: role=dialog, aria-modal, focus trap, Escape handler
- EmailCard: role=article, keyboard nav (Enter/Space), aria-current
- ThreadList: role=list with listitem wrappers, role=status empty state
- FolderNavigation: role=navigation, role=list, aria-current
- RecipientInput: role=group, aria-label per type, onKeyPress→onKeyDown
- BodyEditor: role=toolbar, aria-pressed on mode buttons
- StarButton: MaterialIcon (star/star_border), dynamic aria-label
- MarkAsReadCheckbox: dynamic label based on read state
- EmailHeader: role=banner, <time> element, data-testids

Component decomposition:
- Extract useEmailClient hook (state + callbacks)
- Extract demo-emails.ts (data constants)
- EmailClientContent.tsx: 267→127 LOC (composition only)

New: useKeyboardShortcuts hook (Gmail-style, 47 LOC)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 15:51:54 +00:00

65 lines
1.5 KiB
TypeScript

// fakemui/react/components/email/inputs/BodyEditor.tsx
import React, { forwardRef } from 'react'
import { Box } from '../../layout/Box'
import { useAccessible } from '../../../../hooks/useAccessible'
export interface BodyEditorProps
extends React.TextareaHTMLAttributes<
HTMLTextAreaElement
> {
mode?: 'plain' | 'html'
onModeChange?: (mode: 'plain' | 'html') => void
testId?: string
}
export const BodyEditor = forwardRef<
HTMLTextAreaElement,
BodyEditorProps
>(({
mode = 'plain',
onModeChange,
testId: customTestId,
...props
}, ref) => {
const accessible = useAccessible({
feature: 'email',
component: 'body-editor',
identifier: customTestId || 'body',
ariaLabel: 'Email body'
})
const modeBtn = (m: 'plain' | 'html', label: string) => (
<button
type="button"
className={`mode-btn${mode === m ? ' mode-btn--active' : ''}`}
onClick={() => onModeChange?.(m)}
aria-pressed={mode === m}
data-testid={`mode-${m}`}
>
{label}
</button>
)
return (
<Box className="body-editor">
<div
className="body-editor-toolbar"
role="toolbar"
aria-label="Editor mode"
>
{modeBtn('plain', 'Plain Text')}
{modeBtn('html', 'HTML')}
</div>
<textarea
ref={ref}
className="body-editor-textarea"
placeholder="Write your message here..."
{...accessible}
{...props}
/>
</Box>
)
})
BodyEditor.displayName = 'BodyEditor'