WYSIWYG: a11y 라이브 리전 안내 추가\n- 선택 개수/좌표 및 가이드 좌표 안내(useMemo 기반)\n- a11y 테스트 추가(guides/nudge)\n- 메인플랜 업데이트\n- 전체 테스트 그린

This commit is contained in:
2025-11-17 01:14:30 +09:00
parent 713648fb3a
commit 2070b52023
4 changed files with 134 additions and 0 deletions
+10
View File
@@ -56,6 +56,15 @@ export function Canvas({ frame, onChange }: CanvasProps) {
return null
}, [model, selectedId])
// a11y 라이브 리전 텍스트: 선택 개수 및 가이드 좌표를 폴라이트로 알림
const ariaLiveText = useMemo(() => {
const count = selectedIds.length
const base = `선택 ${count}`
const pos = selected ? `, 위치 x=${selected.x}, y=${selected.y}` : ''
const guide = showGuides ? `, 가이드 x=${guidePos.x}, y=${guidePos.y}` : ''
return `${base}${pos}${guide}`
}, [selectedIds.length, showGuides, guidePos.x, guidePos.y, selected?.x, selected?.y])
const updateObject = useCallback((id: string, updater: (o: WObject) => WObject) => {
setModel((prev) => {
const next: Frame = { ...prev, layers: prev.layers.map((ly) => ({ ...ly, objects: ly.objects.map((o) => (o.id === id ? updater(o) : o)) })) }
@@ -263,6 +272,7 @@ export function Canvas({ frame, onChange }: CanvasProps) {
<div data-testid="guide-y" style={{ position: 'absolute', top: guidePos.y, left: 0, right: 0, height: 1, background: '#0ea5e9' }} />
</>
)}
<div role="status" aria-live="polite" data-testid="aria-live" style={{ position: 'absolute', left: -9999, top: 'auto', width: 1, height: 1, overflow: 'hidden' }}>{ariaLiveText}</div>
{model.layers.map((layer) => layer.visible !== false && layer.objects.map((o) => (
<div
key={o.id}