Files
deepseek-harness/packages/client/ui-primitives/tests/icons.spec.tsx
Yif 92dd231ce4 polish(web): 打磨 Web 会话界面的布局、自适应与组件一致性
1. 统一会话列宽度轴:新增 --dsh-chat-content-width(748px),输入框、todo、goal、queue、approval、plan review、ask question 等容器的宽度与边距全部由该变量推导,消除各面板之间的像素漂移,窄视口下的边缘留白也保持一致。
2. 输入框自适应与细节:控制行改为容器查询(460px 阈值以下权限选择器只显示图标+下拉,隐藏文字);卡片圆角 20→22、行内边距调整并整体下移 2px(发送按钮除外);permission/model 触发器统一 24px 圆角;Plan 与 Read Only 间距 +8。
3. 修复浮层菜单溢出:slash 菜单与命令弹层钳制到输入卡片宽度,超长行以省略号截断;Tooltip 增加 12px 视口边缘安全距离。
4. 增加与替换图标:Add provider 改用输入框同款加号图标(IconPlusOutline16),统一图标尺寸与字号。
5. 统一 Settings → Models 组件:补齐按钮 hover 态、select 下拉箭头不再贴边、标题区与 provider 卡片间距 +12。
6. 侧边栏交互:add workspace / group by / create session / 收起侧边栏四个图标按钮增加 500ms 延迟 tooltip(前两个向下弹出);展开态的 New Session 不再重复显示 tooltip;侧边栏窄屏自适应收起逻辑微调。
7. 其他:hero 区 workspace 徽章右移对齐;附带 Agent Note(中英双语)记录共享宽度轴与容器查询的设计取舍。
2026-08-04 14:14:12 +08:00

61 lines
2.7 KiB
TypeScript

// @vitest-environment jsdom
import { cleanup, render } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'
import * as primitives from '@deepseek-ai/dsh-client-ui-primitives'
import { IconApiOutline14, IconArchiveOutline20, IconFolderClose16, IconSendOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
afterEach(cleanup)
// Icon components all share the IconProps signature; the barrel also exports
// non-icon atoms (different props shapes), so filter by prefix BEFORE typing.
const icons = Object.fromEntries(
Object.entries(primitives).filter(([name]) => name.startsWith('Icon')),
) as Record<string, (p: primitives.IconProps) => React.JSX.Element>
const iconNames = Object.keys(icons)
describe('ic_ds_ icon set', () => {
it('exports the full P-I set (46 deepsuite + 17 figma extracts + the hand-authored sparkle)', () => {
expect(iconNames.length).toBe(64)
})
it.each(iconNames)('%s renders an svg with currentColor fills and no hardcoded palette', (name) => {
const Icon = icons[name]!
const { container } = render(<Icon />)
const svg = container.querySelector('svg')
expect(svg).not.toBeNull()
const markup = container.innerHTML
expect(markup).not.toMatch(/#[0-9a-fA-F]{3,8}"/)
expect(markup).toContain('currentColor')
})
it('size and className props land on the root svg', () => {
const { container } = render(<IconSendOutline16 size={20} className="x" />)
const svg = container.querySelector('svg')!
expect(svg.getAttribute('width')).toBe('20')
expect(svg.getAttribute('height')).toBe('20')
expect(svg.classList.contains('x')).toBe(true)
})
it('each glyph defaults to its own drawn size, not one set-wide default', () => {
const api = render(<IconApiOutline14 />)
expect(api.container.querySelector('svg')!.getAttribute('width')).toBe('14')
const folder = render(<IconFolderClose16 />)
expect(folder.container.querySelector('svg')!.getAttribute('width')).toBe('16')
const archive = render(<IconArchiveOutline20 />)
expect(archive.container.querySelector('svg')!.getAttribute('width')).toBe('20')
})
})
describe('FishLogo', () => {
it('renders the fish path in currentColor at the native ratio', () => {
const { container } = render(<primitives.FishLogo />)
const svg = container.querySelector('svg')!
expect(svg.getAttribute('width')).toBe('24')
expect(Number(svg.getAttribute('height'))).toBeCloseTo(17.66, 1)
expect(svg.getAttribute('viewBox')).toBe('0 0 23.16 17.04')
expect(container.querySelectorAll('path')).toHaveLength(1)
expect(container.innerHTML).toContain('currentColor')
expect(container.innerHTML).not.toContain('M0 0L23.16')
})
})