继续收口首页可导航扁平行

新增 PlatformNavigableListItem 统一 desktop flat row 的交互骨架
接入首页搜索结果与桌面最近作品最近浏览入口
补充组件测试首页回归测试并更新收口计划和共享决策记录
This commit is contained in:
2026-06-11 03:13:00 +08:00
parent 54ff839b0b
commit de0f0c1399
6 changed files with 224 additions and 38 deletions

View File

@@ -0,0 +1,48 @@
/* @vitest-environment jsdom */
import { fireEvent, render, screen } from '@testing-library/react';
import { describe, expect, test, vi } from 'vitest';
import { PlatformNavigableListItem } from './PlatformNavigableListItem';
describe('PlatformNavigableListItem', () => {
test('renders shared navigable row structure with leading and trailing slots', () => {
render(
<PlatformNavigableListItem
align="start"
leading={<span data-testid="leading">L</span>}
trailing={<span data-testid="trailing">R</span>}
>
<span></span>
</PlatformNavigableListItem>,
);
const button = screen.getByRole('button', { name: /L\s+\s+R/u });
expect(button.className).toContain('platform-navigable-list-item');
expect(button.className).toContain('items-start');
expect(button.className).toContain('gap-4');
expect(screen.getByTestId('leading').parentElement?.className).toContain(
'platform-navigable-list-item__leading',
);
expect(screen.getByTestId('trailing').parentElement?.className).toContain(
'platform-navigable-list-item__trailing',
);
});
test('defaults to type button and forwards click handlers', () => {
const onClick = vi.fn();
render(
<PlatformNavigableListItem onClick={onClick}>
<span></span>
</PlatformNavigableListItem>,
);
const button = screen.getByRole('button', { name: '打开详情' });
expect(button.getAttribute('type')).toBe('button');
fireEvent.click(button);
expect(onClick).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,88 @@
import type { ComponentPropsWithoutRef, ReactNode } from 'react';
type PlatformNavigableListItemAlign = 'center' | 'start';
type PlatformNavigableListItemProps = Omit<
ComponentPropsWithoutRef<'button'>,
'children' | 'type'
> & {
children: ReactNode;
leading?: ReactNode;
trailing?: ReactNode;
align?: PlatformNavigableListItemAlign;
leadingClassName?: string;
trailingClassName?: string;
bodyClassName?: string;
};
const ALIGN_CLASS: Record<PlatformNavigableListItemAlign, string> = {
center: 'items-center gap-3',
start: 'items-start gap-4',
};
/**
* 轻量可导航列表行骨架。
* 只统一 button 语义与 left-content/right-affordance 结构不持有业务文案、badge 或封面规则。
*/
export function PlatformNavigableListItem({
children,
leading = null,
trailing = null,
align = 'center',
className,
leadingClassName,
trailingClassName,
bodyClassName,
...props
}: PlatformNavigableListItemProps) {
return (
<button
type="button"
className={[
'platform-navigable-list-item flex w-full min-w-0 text-left',
ALIGN_CLASS[align],
className ?? null,
]
.filter(Boolean)
.join(' ')}
{...props}
>
{leading ? (
<div
className={[
'platform-navigable-list-item__leading shrink-0',
leadingClassName ?? null,
]
.filter(Boolean)
.join(' ')}
>
{leading}
</div>
) : null}
<div
className={[
'platform-navigable-list-item__body min-w-0 flex-1',
bodyClassName ?? null,
]
.filter(Boolean)
.join(' ')}
>
{children}
</div>
{trailing ? (
<div
className={[
'platform-navigable-list-item__trailing shrink-0',
trailingClassName ?? null,
]
.filter(Boolean)
.join(' ')}
>
{trailing}
</div>
) : null}
</button>
);
}