generated from ttipo/project-base
feat(frontend): 읽기 화면 이동 · 끌어다 놓기 · 메일함 옮기기 · 차단할 때 받은편지함 정리 · 예약 메일 지금 보내기
- 읽기 화면 [이동 ▾](옮긴 뒤 다음 메일 · [되돌리기]), 도구 줄은 읽기 칸 폭(컨테이너 쿼리)으로 아이콘만 - [이동 ▾] 메뉴 들여쓰기 · 그 메일이 든 메일함에 하위가 있으면 흐린 줄 · 넘치면 좌우 바꾸기(EditorPopup 메뉴 모두), 같은 이름이 다른 곳에도 있을 때만 알림에 "상위 › 이름" - 목록 → 메일함 패널 끌어다 놓기(고른 것 · 대화 줄, 휴지통은 삭제), 메일 화면에 놓은 파일은 무시 - 스팸메일함으로 옮기는 세 길 같게 — 메뉴 · 끌기도 스팸 표시를 붙이고, 꺼내면 스팸 아님 - 메일함 ⋯ 옮기기 창(같은 이름이 있는 곳은 처음부터 막음) · 하위까지 지우기 확인 창 - 차단 창에 받은편지함 정리 칸(같은 탭의 필터 적용 · 스팸메일함 비우기와 함께 돌지 않음), 진행 알림 하나 - 예약 안내 줄 [지금 보내기] — 결과를 모르는 실패는 그 메일을 다시 받아 가르고 확인하지 못한 일은 단정하지 않는다 - 상단 바 좁은 폭에서 [로그아웃]이 꺾이지 않게, 같은 대화 [모두 펼치기] · 펼친 줄 머리 [답장] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// 단위 테스트 전용 — 끌어다 놓기 이벤트 (jsdom 에는 DragEvent · DataTransfer 가 없다).
|
||||
// 브라우저처럼 dragstart 에 넣은 값은 놓을 때만 읽히고(getData), 끄는 동안(dragenter · dragover)에는 종류(types)만 보인다 —
|
||||
// 테스트도 같은 DataTransfer 를 dragstart · dragover · drop 에 함께 넘긴다. 앱 코드에서 import 하지 않는다
|
||||
|
||||
export interface FakeDataTransfer {
|
||||
types: string[]
|
||||
files: File[]
|
||||
effectAllowed: string
|
||||
dropEffect: string
|
||||
setData(type: string, value: string): void
|
||||
getData(type: string): string
|
||||
/** 파일 밖의 값을 모두 지운다 (브라우저처럼 — 끌기를 시작할 때만 쓸 수 있다) */
|
||||
clearData(): void
|
||||
setDragImage(image: Element, x: number, y: number): void
|
||||
/** setDragImage 로 붙인 그림 (테스트가 본다) */
|
||||
dragImage: Element | null
|
||||
}
|
||||
|
||||
/** 메일 끌기에 쓰는 빈 DataTransfer — 파일 끌기면 files 를 넘긴다(types 에 'Files') */
|
||||
export function fakeDataTransfer(files: File[] = []): FakeDataTransfer {
|
||||
const data = new Map<string, string>()
|
||||
const transfer: FakeDataTransfer = {
|
||||
types: files.length > 0 ? ['Files'] : [],
|
||||
files,
|
||||
effectAllowed: 'uninitialized',
|
||||
dropEffect: 'none',
|
||||
dragImage: null,
|
||||
setData(type, value) {
|
||||
data.set(type, value)
|
||||
if (!transfer.types.includes(type)) transfer.types.push(type)
|
||||
},
|
||||
getData(type) {
|
||||
return data.get(type) ?? ''
|
||||
},
|
||||
clearData() {
|
||||
data.clear()
|
||||
transfer.types = transfer.types.filter((type) => type === 'Files')
|
||||
},
|
||||
setDragImage(image) {
|
||||
transfer.dragImage = image
|
||||
},
|
||||
}
|
||||
return transfer
|
||||
}
|
||||
|
||||
/** dragstart · dragenter · dragover · dragleave · drop · dragend — bubbles · cancelable, 마우스 자리(clientX · clientY)는 0 */
|
||||
export function dragEvent(
|
||||
type: string,
|
||||
dataTransfer: FakeDataTransfer,
|
||||
init: { relatedTarget?: EventTarget | null; ctrlKey?: boolean } = {},
|
||||
): Event {
|
||||
const event = new Event(type, { bubbles: true, cancelable: true })
|
||||
Object.defineProperty(event, 'dataTransfer', { value: dataTransfer })
|
||||
Object.defineProperty(event, 'relatedTarget', { value: init.relatedTarget ?? null })
|
||||
Object.defineProperty(event, 'clientX', { value: 0 })
|
||||
Object.defineProperty(event, 'clientY', { value: 0 })
|
||||
Object.defineProperty(event, 'ctrlKey', { value: init.ctrlKey ?? false })
|
||||
return event
|
||||
}
|
||||
Reference in New Issue
Block a user