fix(frontend): 같은 대화 칸의 안 읽은 줄을 굵게 · 패널 [조건 지우기]는 칸만 비운다

2026-11-07 사용자가 사용자 관점 점검의 보드 바꾸기 제안을 추천대로 정했다.
- 같은 대화 칸: 안 읽은 줄을 목록 줄처럼 굵게 그리고 화면 낭독에 "안 읽음, "을
  붙인다. 이 탭이 바꾼 읽음(펼침 · 읽기 화면 · 목록 · 여러 통 처리 · 모두 읽음)은
  줄에 바로 적고, 받는 동안 바꾼 것은 받은 값보다 앞세운다. 읽음만 바뀐 소식으로는
  다시 받지 않는다(메일을 열 때마다 다시 받게 되므로).
- 여러 통 처리가 실패하면 바꾼 표시만 되돌린다(중요 표시 실패가 읽음을 덮지 않게).
- 조건 패널의 [조건 지우기]는 칸만 비우고 패널을 열어 둔다(찾지 않는다). 조건만
  걸린 검색에서 비운 채 [찾기]를 누르면 검색을 닫는다.
- 검색 결과 미리 보기 줄의 강조는 글자를 --color-text-secondary 로 진하게 한다
  (노랑 위 대비 4.05:1 → 6.3:1).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-07 10:49:27 +09:00
parent cc4f550044
commit 438dac8e01
21 changed files with 668 additions and 53 deletions
@@ -19,6 +19,8 @@ import {
type MockResult,
} from '@/test-utils/mock-api'
import { createTestRouter } from '@/test-utils/router'
import highlightedTextSource from '@/components/mail/HighlightedText.vue?raw'
import messageListItemSource from '@/components/mail/MessageListItem.vue?raw'
import MailboxPage from './MailboxPage.vue'
// 검색 결과 화면 (디자인 원본 Search · ListStates · 계약 2절 "검색")
@@ -369,6 +371,22 @@ describe('MailboxPage — 14단계 검색 (조건 · 강조 · 이어 찾기 ·
expect(row.find('[aria-label="첨부 있음"]').exists()).toBe(true)
})
it('미리 보기 줄의 맞은 말만 글자색을 진하게(--color-text-secondary) — 흐린 글자 위 노랑은 대비가 모자라다, 제목 줄의 강조는 글자색 그대로 (2026-11-07)', () => {
// jsdom 은 컴포넌트의 style 을 적용하지 않는다 — 규칙을 원본에서 읽는다 (주석은 뺀다)
const rulesOf = (source: string) =>
[...source.slice(source.indexOf('<style')).replace(/\/\*[\s\S]*?\*\//g, '').matchAll(/([^{}]+)\{([^}]*)\}/g)].map(
([, selector = '', body = '']) => ({ selector: selector.replace('<style scoped>', '').trim(), body }),
)
// 강조 자체는 글자색을 물려받는다 — 제목 줄 · 읽기 화면 · 첨부 이름은 진한 글자 위다
const mark = rulesOf(highlightedTextSource).find((rule) => rule.selector === '.search-mark')
expect(mark?.body).toMatch(/color:\s*inherit;/)
// 목록 줄은 미리 보기 줄 안의 강조만 바꾼다 (제목 줄의 강조를 바꾸는 규칙은 없다)
const overrides = rulesOf(messageListItemSource).filter((rule) => rule.selector.includes('search-mark'))
expect(overrides.map((rule) => rule.selector)).toEqual(['.message-row__preview :deep(.search-mark)'])
expect(overrides[0]?.body).toMatch(/color:\s*var\(--color-text-secondary\);/)
})
it('강조가 없으면(엔진 방식 — 색인을 준비하는 중) 글 그대로 — 미리 보기는 메일의 미리 보기, 결과 머리 아래 "검색 준비 중" 줄', async () => {
handler = ({ url }) => {
if (url === '/search-index') return indexState('building')
@@ -466,6 +484,16 @@ describe('MailboxPage — 14단계 검색 (조건 · 강조 · 이어 찾기 ·
expect(document.activeElement).toBe(wrapper.get('section.mail-list-frame').element)
})
it('조건만 걸린 검색에서 조건 줄의 [조건 지우기]는 검색을 닫는다 — 검색 전 메일함으로 (패널의 [조건 지우기]는 칸만 비운다 — 2026-11-07)', async () => {
useMailStore().rememberSearchOrigin({ mailboxId: 'inbox', location: '/mail/inbox' })
const { wrapper, router } = await mountAt(`/mail/search?in=inbox&${CONDITIONS_QUERY}`)
await wrapper.get('[data-search-condition-line-clear]').trigger('click')
await flushPromises()
expect(router.currentRoute.value.fullPath).toBe('/mail/inbox')
})
it('조건 줄의 [조건 지우기]를 누르면 줄이 사라진다 — 초점은 목록 칸으로', async () => {
const { wrapper } = await mountAt(`/mail/search?q=${Q}&in=inbox&${CONDITIONS_QUERY}`, { attach: true })
const clear = wrapper.get('[data-search-condition-line-clear]')