Files
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

77 lines
1.9 KiB
TypeScript

// fakemui/react/components/email/data-display/ThreadList.tsx
import React from 'react'
import { Box, BoxProps } from '../..'
import { useAccessible } from '../../../../hooks/useAccessible'
import { EmailCard, type EmailCardProps } from '../surfaces'
export interface ThreadListProps extends BoxProps {
emails: Array<Omit<
EmailCardProps,
'onSelect' | 'onToggleRead' | 'onToggleStar'
>>
selectedEmailId?: string
onSelectEmail?: (emailId: string) => void
onToggleRead?: (id: string, read: boolean) => void
onToggleStar?: (id: string, star: boolean) => void
testId?: string
}
export const ThreadList = ({
emails,
selectedEmailId,
onSelectEmail,
onToggleRead,
onToggleStar,
testId: customTestId,
...props
}: ThreadListProps) => {
const accessible = useAccessible({
feature: 'email',
component: 'thread-list',
identifier: customTestId || 'threads'
})
const emailId = (e: typeof emails[0], i: number) =>
e.testId || `email-${i}`
return (
<Box
role="list"
aria-label="Email messages"
className="thread-list"
{...accessible}
{...props}
>
{emails.length === 0 ? (
<div className="no-emails" role="status">
No emails
</div>
) : (
emails.map((email, idx) => (
<div role="listitem" key={idx}>
<EmailCard
{...email}
selected={
selectedEmailId === email.testId
}
onSelect={() =>
onSelectEmail?.(emailId(email, idx))
}
onToggleRead={(read) =>
onToggleRead?.(
emailId(email, idx), read
)
}
onToggleStar={(starred) =>
onToggleStar?.(
emailId(email, idx), starred
)
}
/>
</div>
))
)}
</Box>
)
}