Tree 拖拽视觉与行布局对齐新版 Figma(DES-89)
Pre-release · 类型: ✨ 新功能
Linear: DES-89
Figma:Design System 新增独立 Tree 页面(canvas 24402:2236),Tree Item State 轴扩展为 Default / Hover / Selected / Dragged Source / Drop Indicator 5 态,新增 Hierarchy 轴与 Drag Preview / Drop Indicator 元素。
改动文件
src/components/Tree/styles.tssrc/components/Tree/Tree.tsxsrc/components/Tree/Tree.test.tsxsrc/components/Tree/TreeDesignSpec.mdpackages/design-site/docs/components/patterns/tree.mdx(+ 中文版)
改动内容
1. 行布局 token
- 行内 gap:
gap-(--Spacing_12)→gap-(--Spacing_8) - 行内边距:
px-(--Spacing_8)→pl-(--Spacing_8) pr-(--Spacing_4) - actions 容器:
right-(--Spacing_8)→right-(--Spacing_4)
2. 层级缩进
indent 默认 24 → 16;缩进公式从 level * indent 改为 level * indent - 4(首层子级 12px,此后每层 +16px),对齐 Figma Hierarchy 轴(Item 与 Drop Indicator 两组组件均验证为「首层 +12、其后 +16」规律)。
3. Dragged Source 状态
拖拽源行从 opacity-50 改为 1px 内描边 inset-ring inset-ring-(--Separators-Emphasized),内容保持完整不透明(Figma 24448:14150)。
4. Drop 指示器
行间投放指示器从「行内 2px --Labels-Primary 横线」改为「行间 4px gap 内 --Labels-Link ∅8 空心圆点(2px 描边)+ 2px 横线」(Figma Tree Element - Drop Indicator 24459:4888):
- 容器
-top-1/-bottom-1落在 rootgap-1的行间空隙 - 左缘 = 本级 icon 左缘 + 8px(随 Hierarchy 缩进),右缘距行右 4px
5. 拖拽预览(新增)
dragstart 时克隆行内 icon + title 构建白底浮层(--Grays-White + 0 0 32px var(--Effects-Shadow-Default) 阴影 + 88% 不透明度,宽度与行一致),经 setDragImage 替换浏览器默认行截图(Figma Tree Element - Drag Preivew 24448:14181);截图后下一帧移除节点。
setDragImage 不存在或调用抛错的环境(如 happy-dom 存在该方法但未实现)回退默认拖拽影像并同步移除预览节点,避免泄漏。
追加:视觉对比 case(同日)
Linear: DES-90
新增 Tree.visual.spec.tsx(Playwright CT,9 个 case)+ TreeFixture.visual.tsx(场景 fixture),并在 scripts/visual-diff-config.ts 注册 Tree 的 Figma 节点映射:
- case:default / expanded / selected / leaf / hierarchy-2 / hover / dragged-source / drop-indicator / drag-preview,与 Figma Tree 页面变体一一对应(映射表见
TreeDesignSpec.md§9) - Playwright CT 的 spec 只能传可序列化 props,icon / actions / 拖拽配置收敛进 fixture(沿用 Toast 的
ToastFixture.visual.tsx模式) - 拖拽态用
dispatchEvent+ 页面内真实DataTransfer驱动(Playwright 无原生 HTML5 DnD);drag preview 因真实节点随 dragstart 下一帧销毁,改为用TREE_DRAG_PREVIEW_CLASS静态复刻 vitest.config.tscoverage exclude 补充*.visual.spec.tsx/*.visual.tsx(视觉测试文件不计覆盖率)- 已验证:9 case 截图基线稳定(两次运行一致),
pnpm visual:gallery中 Tree 9 个 Figma 节点全部拉图配对成功
追加:超长 title 自动 tooltip(同日)
Linear: DES-91
title 内容超出容器宽度时自动用 Tooltip(悬停延迟 300ms)包裹展示完整内容;未超长不引入 tooltip。
- 超长判定:克隆 title 节点离屏渲染(fixed + hidden,挂同一父级保留字体级联),用克隆体自然宽度对比容器
clientWidth(+1px 容差);不用scrollWidth(title 是 mask 渐变非 ellipsis,且内容可为任意 ReactNode)。实现见utils.measureTitleOverflow - 重测时机:挂载后一次 + ResizeObserver 监听容器尺寸变化 + title 内容变化
- 踩坑记录:RO 必须观察稳定的行容器,且测量回调里实时读
titleRef.current—— tooltip 包裹会重建 title 节点,若 RO 观察 title 节点本身并在闭包捕获旧引用,旧节点卸载时 RO 会测到脱离文档的 0 宽,把 overflowed 错误重置回 false(真实浏览器中 CT 测试抓到的回归) - 测试:单测 7 个(测量函数 4 + 组件行为 3,happy-dom 下用原型 getter 注入宽度);CT 行为测试 1 个(真实布局下长 title hover 出 tooltip、短 title 不包 trigger)
根因(测试环境)
design 包 vitest 环境为 happy-dom:e.dataTransfer 是 happy-dom 自有 DataTransfer 实例(非 fireEvent 传入的 mock),其 setDragImage 存在但调用即抛错。首版实现仅做 typeof 守卫导致预览节点泄漏进 document.body,污染后续 39 个用例;改为 try/catch 回退后解决。测试侧通过 vi.spyOn(DataTransfer.prototype, 'setDragImage') 验证预览构建与注入。