Files
Kanban/src/components/AppNav.spec.ts
T
chenshoulin 442cbbd734 style: 重做界面视觉与信息层级
先截图审视现状,再针对性修改。主要问题与改动如下。

工具栏
- 标签筛选从「平铺全部标签」改为下拉面板,标签多时不再换行挤爆工具栏
- 备份与快捷键帮助移到导航栏 —— 它们是应用级操作,不该挤在看板工具栏里,
  顺带修好了「? 帮助只在看板可用」的问题
- 到期统计(今日到期 / 逾期)合并为可点击的筛选入口,去掉两个独立控件
- 移动端工具栏从占半屏降到三行紧凑布局

卡片
- 优先级从彩色胶囊改为左侧色条,扫视一列时比读文字更快分辨轻重
- 元信息改为「优先级 · 日期 · 标签」的轻量文本行,不再三种胶囊叠在一起
- 完成态用勾选图标 + 降低不透明度

布局
- 今日视图限宽居中(max-w-3xl):此前 1440px 下卡片被拉满整屏,
  标题与元信息之间出现大片空白
- 修复移动端卡片被压成细条:TaskCard 的 overflow-hidden(用于裁切左侧色条)
  会让 flex 项的最小尺寸解析为 0,补 shrink-0 修正;
  同时改为移动端按内容高度堆叠、桌面端列内滚动

其他
- 导航栏品牌改用看板图标,消除「看板」重复出现两次
- 统一卡片阴影、圆角与键盘焦点样式
- 新增 scripts/screenshot.mjs,便于后续对照审视视觉改动

验证:单元测试 452 个、端到端测试 23 个全部通过,lint 与构建无错误。
2026-10-18 14:05:06 +08:00

87 lines
2.7 KiB
TypeScript

import { flushPromises, mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { createMemoryHistory, createRouter } from 'vue-router'
import { describe, expect, it } from 'vitest'
import { routes } from '@/router'
import { useUiStore } from '@/stores/ui'
import AppNav from './AppNav.vue'
async function setup(initialPath = '/') {
const pinia = createPinia()
setActivePinia(pinia)
const router = createRouter({ history: createMemoryHistory(), routes })
await router.push(initialPath)
await router.isReady()
const wrapper = mount(AppNav, {
global: { plugins: [pinia, router] },
attachTo: document.body,
})
return { wrapper, router, ui: useUiStore() }
}
describe('AppNav 导航', () => {
it('渲染看板与今日两个入口', async () => {
const { wrapper } = await setup()
expect(wrapper.findAll('a').map((node) => node.text())).toEqual(['看板', '今日'])
})
it('当前路由的入口标记 aria-current', async () => {
const { wrapper } = await setup('/')
const links = wrapper.findAll('a')
expect(links[0]?.attributes('aria-current')).toBe('page')
expect(links[1]?.attributes('aria-current')).toBeUndefined()
})
it('在今日页时高亮今日入口', async () => {
const { wrapper } = await setup('/today')
const links = wrapper.findAll('a')
expect(links[0]?.attributes('aria-current')).toBeUndefined()
expect(links[1]?.attributes('aria-current')).toBe('page')
})
it('点击可切换路由', async () => {
const { wrapper, router } = await setup('/')
await wrapper.findAll('a')[1]?.trigger('click')
// 路由跳转是异步的,必须等 Promise 落地
await flushPromises()
expect(router.currentRoute.value.path).toBe('/today')
})
it('导航带可访问性标签', async () => {
const { wrapper } = await setup()
expect(wrapper.find('nav').attributes('aria-label')).toBe('主导航')
})
})
describe('AppNav 应用级操作', () => {
it('点击「备份」打开备份面板', async () => {
const { wrapper, ui } = await setup()
expect(ui.backupOpen).toBe(false)
const backupButton = wrapper.findAll('button').find((node) => node.text() === '备份')
await backupButton?.trigger('click')
expect(ui.backupOpen).toBe(true)
})
it('点击 ? 打开快捷键帮助', async () => {
const { wrapper, ui } = await setup()
await wrapper.find('button[aria-label="快捷键帮助"]').trigger('click')
expect(ui.helpOpen).toBe(true)
})
it('应用级操作在看板与今日视图下都可用', async () => {
const { wrapper } = await setup('/today')
expect(wrapper.findAll('button').map((node) => node.text())).toContain('备份')
})
})