返修资源卡完整名悬停入口并收短文档卡注释

- 完整资源名改挂在覆盖整卡、真正命中指针的选中按钮 title 上,名称条自身不再挂悬停不到的 title

- 无障碍名保持原有 aria-label 不变,长名仍是 CSS 单行省略

- 断言改为校验选中按钮 title,并钉住名称条不挂 title

- 文档卡分支注释只写当前行为,去掉旧实现说明
This commit is contained in:
2026-09-17 17:36:50 +08:00
parent 3ba6168c6a
commit 5c31ae91ca
4 changed files with 31 additions and 25 deletions
@@ -981,11 +981,7 @@ const ResourceCard = memo(function ResourceCard({
</span>
);
}
/**
* 文档卡:居中图标,**不显示正文摘要**(旧实现把前 3 行正文铺在卡面上,
* 与"卡面不显示无效正文片段"冲突)。原文预览、代码块与 UI JSON 识别都留在
* 独立详情浮层,卡面这条链路不再读内容做展示。
*/
/** 文档卡:卡面只给居中的文档图标,不铺正文。 */
if (isDocumentCard) {
return (
<span className="game-resource-card-document-visual">
@@ -1079,13 +1075,13 @@ const ResourceCard = memo(function ResourceCard({
它来自现有正式命名链路——manifest 资产的 `localPath` 就是生成时的
`assetName` 落盘名,用户重命名也会改写同一份 `localPath`,因此这里消费
投影 label 即可,不从临时输入或历史任务名另取一份显示名,也不持久化第二份
名称。名称长了由 CSS 单行省略,完整名称在 `title` 上;`data-resource-name`
名称。名称长了由 CSS 单行省略,完整名称挂在整卡选中按钮的 `title` 上
(名称条自身 `pointer-events: none`,悬停不到);`data-resource-name`
是给端到端验收的稳定判据。
*/}
<span
className="game-resource-card-name"
data-resource-name={resource.label}
title={resource.label}
>
{resource.label}
</span>
@@ -1119,7 +1115,9 @@ const ResourceCard = memo(function ResourceCard({
aria-label={`选中资源:${categoryLabels[resource.category]} ${resource.label}`}
aria-pressed={selected}
data-resource-id={resource.id}
title="选中资源"
// 名称条 `pointer-events: none`,鼠标命中的是覆盖整卡的这颗按钮,
// 所以「长名可悬停读全」的完整名挂在这里;无障碍名仍是上面的 aria-label。
title={resource.label}
/>
{isMedia ? (
<button
@@ -2980,11 +2980,15 @@ export function registerProjectWorkbenchFoundationTests() {
let heroDetailButton = await findResourceSelectButton('hero.png');
const heroCard = heroDetailButton.closest('.game-resource-card');
// 卡面名称就是正式资源名(manifest `localPath` 的文件名,生成时来自 assetName、
// 重命名时同步改写),长名由 CSS 省略、完整名在 `title` 上。
// 重命名时同步改写),长名由 CSS 省略、完整名挂在实际命中指针的整卡选中按钮 `title` 上。
const heroName = heroCard?.querySelector('.game-resource-card-name');
expect(heroName?.textContent).toBe('hero.png');
expect(heroName?.getAttribute('title')).toBe('hero.png');
expect(heroName?.getAttribute('data-resource-name')).toBe('hero.png');
expect(
heroCard
?.querySelector('.game-resource-card-select')
?.getAttribute('title'),
).toBe('hero.png');
// 卡面仍然只给"名称 + 类型",不铺完整路径与来源这类详细文本。
expect(heroCard?.textContent).not.toContain('assets/hero.png');
expect(heroCard?.textContent).not.toContain('Agent 生成');
@@ -4265,9 +4269,9 @@ export function registerProjectWorkbenchFoundationTests() {
expect(screen.queryByRole('toolbar', { name: '图片工具栏' })).toBeNull();
});
expect(
screen
.queryAllByTitle('选中资源')
.some((card) => card.getAttribute('aria-pressed') === 'true'),
Array.from(
document.querySelectorAll<HTMLElement>('.game-resource-card-select'),
).some((card) => card.getAttribute('aria-pressed') === 'true'),
).toBe(false);
});
@@ -202,15 +202,20 @@ async function openResourceBookCategory(label: string) {
);
}
/** 卡面名称节点断言:文本、`title` 与稳定 DOM 判据都是同一个正式资源名。 */
/**
* 卡面名称断言:名称条文本与稳定 DOM 判据是同一个正式资源名,**完整名挂在整卡选中按钮的
* `title` 上** —— 名称条 `pointer-events: none`,只有这颗覆盖整卡的按钮命中指针,
* 「悬停读全名」必须落在它身上才真的会出 tooltip。
*/
async function expectCardName(name: string) {
const card = (await findResourceSelectButton(name)).closest<HTMLElement>(
'.game-resource-card',
)!;
const selectButton = await findResourceSelectButton(name);
const card = selectButton.closest<HTMLElement>('.game-resource-card')!;
const nameNode = card.querySelector('.game-resource-card-name');
expect(nameNode?.textContent).toBe(name);
expect(nameNode?.getAttribute('title')).toBe(name);
expect(nameNode?.getAttribute('data-resource-name')).toBe(name);
// 名称条不挂 `title`:它悬停不到,挂上去就是一条永远不触发的死提示。
expect(nameNode?.getAttribute('title')).toBeNull();
expect(selectButton.getAttribute('title')).toBe(name);
return card;
}
@@ -231,16 +231,15 @@ describe('素材重命名前端链路', () => {
rendered.rerenderWith(renamedManifest);
await waitFor(() => {
const renamedCard = screen
.getByRole('button', {
name: '选中资源:角色与对象 hero-v2.png',
})
.closest('.game-resource-card');
const renamedSelect = screen.getByRole('button', {
name: '选中资源:角色与对象 hero-v2.png',
});
const renamedCard = renamedSelect.closest('.game-resource-card');
// 卡面名称与重命名后的 manifest `localPath` 同步:名称就是正式文件名的投影,
// 不存在第二份需要一起改的显示名。
// 不存在第二份需要一起改的显示名;完整名挂在命中指针的选中按钮 `title` 上。
const nameNode = renamedCard?.querySelector('.game-resource-card-name');
expect(nameNode?.textContent).toBe('hero-v2.png');
expect(nameNode?.getAttribute('title')).toBe('hero-v2.png');
expect(renamedSelect.getAttribute('title')).toBe('hero-v2.png');
});
});