后台模板上传弹窗表头层级修正与仓库卫生
- AdminAgcTemplatesPage 的上传弹窗把 TableHeader(thead)当列头使用,生成出 th > tr > thead 的非法层级;修正为 TableHeader > TableRow > TableHead,并补列头角色与层级回归。 - .gitignore 补 /server-rs/crates/*/server-rs/.data/:api-server 的相对 outbox 默认路径在嵌套 cwd 下会在仓库内生成内嵌数据目录,避免其被误提交。
This commit is contained in:
@@ -65,6 +65,9 @@ temp*build*/
|
||||
/apps/preview-deployer-web/node_modules/
|
||||
/server-rs/.spacetimedb/
|
||||
/server-rs/.data/
|
||||
# api-server 的 tracking / wallet-refund outbox 默认目录相对仓库根(server-rs/.data/…);
|
||||
# 从 server-rs/crates/<crate> 目录运行测试或二进制时,会在仓库内生成嵌套副本,不能提交。
|
||||
/server-rs/crates/*/server-rs/.data/
|
||||
/public/generated-animations
|
||||
/public/generated-character-drafts
|
||||
/public/generated-characters
|
||||
|
||||
@@ -542,6 +542,37 @@ function pickUploadFiles(dialog: HTMLElement, accept: string, files: File[]) {
|
||||
fireEvent.change(input, { target: { files } });
|
||||
}
|
||||
|
||||
test('批量上传:表头是真实列头单元格,不是嵌套 thead', async () => {
|
||||
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
|
||||
const dialog = await openUploadDialog();
|
||||
pickUploadFiles(dialog, '.zip,application/zip', [uploadZipFile('alpha.zip')]);
|
||||
|
||||
const table = Array.from(dialog.querySelectorAll('table')).find((candidate) =>
|
||||
candidate.textContent?.includes('alpha.zip'),
|
||||
);
|
||||
expect(table).toBeTruthy();
|
||||
|
||||
// 每一列表头都必须是 `th`:把 `<TableHeader>`(渲染 `<thead>`)当单元格用会产出
|
||||
// `<th><tr><thead>`,列头语义与样式一起丢失。
|
||||
expect(
|
||||
within(table as HTMLElement)
|
||||
.getAllByRole('columnheader')
|
||||
.map((cell) => cell.textContent),
|
||||
).toEqual([
|
||||
'模板包',
|
||||
'ID',
|
||||
'名称',
|
||||
'版本',
|
||||
'运行时',
|
||||
'entry',
|
||||
'封面',
|
||||
'操作',
|
||||
]);
|
||||
expect(table?.querySelectorAll('thead thead')).toHaveLength(0);
|
||||
expect(table?.querySelectorAll('thead > tr > th')).toHaveLength(8);
|
||||
expect(table?.querySelectorAll('thead > th')).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('批量上传:多选 ZIP 生成行,封面匹配齐了才能提交', async () => {
|
||||
render(<AdminAgcTemplatesPage token="token" onUnauthorized={vi.fn()} />);
|
||||
const dialog = await openUploadDialog();
|
||||
|
||||
@@ -880,18 +880,18 @@ function TemplateUploadDialog({
|
||||
) : (
|
||||
<div className="admin-agc-template-upload-scroll">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeader>模板包</TableHeader>
|
||||
<TableHeader>ID</TableHeader>
|
||||
<TableHeader>名称</TableHeader>
|
||||
<TableHeader>版本</TableHeader>
|
||||
<TableHeader>运行时</TableHeader>
|
||||
<TableHeader>entry</TableHeader>
|
||||
<TableHeader>封面</TableHeader>
|
||||
<TableHeader>操作</TableHeader>
|
||||
<TableHead>模板包</TableHead>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>名称</TableHead>
|
||||
<TableHead>版本</TableHead>
|
||||
<TableHead>运行时</TableHead>
|
||||
<TableHead>entry</TableHead>
|
||||
<TableHead>封面</TableHead>
|
||||
<TableHead>操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{state.rows.map((row) => {
|
||||
const rowError = state.errors[row.key];
|
||||
|
||||
Reference in New Issue
Block a user