style: refine dense task views
This commit is contained in:
@@ -8,7 +8,7 @@ const memo = { id: 'm1', title: '旅行清单', excerpt: '护照\n 充电器
|
||||
afterEach(() => cleanups.splice(0).forEach((cleanup) => cleanup()))
|
||||
|
||||
describe('MemoRow', () => {
|
||||
it('renders a compact selectable row with plain excerpt and semantic update time', async () => {
|
||||
it('renders only the title and semantic update time in a selectable row', async () => {
|
||||
const host = document.createElement('div'); document.body.append(host)
|
||||
const selected: Array<{ id: string; opener: EventTarget | null }> = []
|
||||
const app = createApp(() => h(MemoRow, { memo, active: true, onSelect: (id: string, opener: EventTarget | null) => selected.push({ id, opener }) }))
|
||||
@@ -16,31 +16,15 @@ describe('MemoRow', () => {
|
||||
const button = host.querySelector('button')!
|
||||
expect(button.classList.contains('active')).toBe(true)
|
||||
expect(button.getAttribute('aria-current')).toBe('true')
|
||||
expect(host.querySelector('.memo-row__excerpt')?.textContent).toBe('护照 充电器 相机')
|
||||
expect(button.querySelectorAll(':scope > *')).toHaveLength(2)
|
||||
expect(host.querySelector('.memo-row__excerpt')).toBeNull()
|
||||
expect(button.textContent).not.toContain('护照')
|
||||
expect(host.querySelector('time')?.getAttribute('datetime')).toBe(memo.updated_at)
|
||||
button.click()
|
||||
expect(selected).toHaveLength(1)
|
||||
expect(selected[0]).toEqual({ id: 'm1', opener: button })
|
||||
})
|
||||
|
||||
it('updates the excerpt when the memo prop changes and preserves empty and whitespace formatting', async () => {
|
||||
const host = document.createElement('div'); document.body.append(host)
|
||||
const current = ref({ ...memo, excerpt: '' })
|
||||
const app = createApp(() => h(MemoRow, { memo: current.value }))
|
||||
app.mount(host); cleanups.push(() => { app.unmount(); host.remove() }); await nextTick()
|
||||
|
||||
const excerpt = () => host.querySelector('.memo-row__excerpt')?.textContent
|
||||
expect(excerpt()).toBe('暂无正文')
|
||||
|
||||
current.value = { ...current.value, excerpt: '保存后\n 新摘要 正常' }
|
||||
await nextTick()
|
||||
expect(excerpt()).toBe('保存后 新摘要 正常')
|
||||
|
||||
current.value = { ...current.value, excerpt: ' \n\t ' }
|
||||
await nextTick()
|
||||
expect(excerpt()).toBe('暂无正文')
|
||||
})
|
||||
|
||||
it('updates the displayed timestamp when the memo prop changes', async () => {
|
||||
const host = document.createElement('div'); document.body.append(host)
|
||||
const current = ref({ ...memo })
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
export type MemoListItem = { id: string; title: string; excerpt: string; version: number; created_at: string; updated_at: string; deleted_at: string | null }
|
||||
export type MemoListItem = { id: string; title: string; excerpt?: string; version: number; created_at: string; updated_at: string; deleted_at: string | null }
|
||||
const props = defineProps<{ memo: MemoListItem; active?: boolean }>()
|
||||
const emit = defineEmits<{ select: [id: string, opener: EventTarget | null] }>()
|
||||
const excerpt = computed(() => props.memo.excerpt.replace(/\s+/g, ' ').trim())
|
||||
const updated = computed(() => new Intl.DateTimeFormat('zh-CN', { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(props.memo.updated_at)))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="memo-row" :class="{ active }" :aria-current="active ? 'true' : undefined" @click="emit('select', memo.id, $event.currentTarget)">
|
||||
<strong>{{ memo.title }}</strong>
|
||||
<span class="memo-row__excerpt">{{ excerpt || '暂无正文' }}</span>
|
||||
<time :datetime="memo.updated_at">{{ updated }}</time>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user