收口个人中心已玩弹窗组件

迁移个人中心存档和玩过弹窗简单空态到 PlatformEmptyState

迁移玩过弹窗分区标题到 PlatformFieldLabel

迁移已玩作品按钮卡到 PlatformSubpanel 并保留粉色 hover 边框

补充已玩弹窗公共组件断言并更新 PlatformUiKit 文档和 Hermes 决策记录
This commit is contained in:
2026-06-10 11:59:15 +08:00
parent 076970828a
commit b507302fdb
5 changed files with 80 additions and 11 deletions

View File

@@ -11646,6 +11646,11 @@ test('profile page keeps save archives inside played stats panel', async () => {
await openProfilePlayedWorks(user);
const continueSectionLabel = screen.getByText('可继续');
expect(continueSectionLabel.className).toContain('tracking-[0.18em]');
expect(continueSectionLabel.className).toContain(
'text-[var(--platform-text-soft)]',
);
expect(screen.queryByLabelText('关闭存档')).toBeNull();
expect(screen.queryByText('SAVES')).toBeNull();
await clickFirstAsyncButtonByName(user, /潮雾列岛/u);

View File

@@ -2759,6 +2759,22 @@ test('profile played modal summary and work type use platform pill badges', asyn
const playedModal = screen
.getByText('潮雾列岛')
.closest('.fixed') as HTMLElement;
const playedSectionLabel = within(playedModal)
.getAllByText('玩过')
.find((element) => element.className.includes('tracking-[0.18em]'));
expect(playedSectionLabel?.className).toContain(
'text-[var(--platform-text-soft)]',
);
const playedWorkButton = within(playedModal)
.getByText('潮雾列岛')
.closest('button') as HTMLElement;
expect(playedWorkButton.className).toContain(
'border-[var(--platform-subpanel-border)]',
);
expect(playedWorkButton.className).toContain('hover:border-[#ff4056]');
expect(playedWorkButton.className).toContain('focus-visible:ring-2');
const totalPlayTimeBadge = within(playedModal).getByText('1.5小时');
expect(totalPlayTimeBadge.className).toContain('rounded-full');
expect(totalPlayTimeBadge.className).toContain('border-rose-100');
@@ -2770,6 +2786,32 @@ test('profile played modal summary and work type use platform pill badges', asyn
expect(workTypeBadge.className).toContain('text-[#ff4056]');
});
test('profile played modal empty state uses platform empty state', async () => {
renderProfileView(
vi.fn(),
{
playedWorldCount: 0,
},
{},
0,
{
isProfilePlayStatsOpen: true,
profilePlayStats: {
totalPlayTimeMs: 0,
playedWorks: [],
updatedAt: '2026-04-19T12:00:00.000Z',
},
},
);
const emptyState = await screen.findByText('暂无玩过');
expect(emptyState.className).toContain('platform-empty-state');
expect(emptyState.className).toContain(
'border-[var(--platform-subpanel-border)]',
);
expect(emptyState.className).toContain('text-left');
});
test('profile played works card shows count unit', async () => {
renderProfileView(vi.fn(), {
playedWorldCount: 1,

View File

@@ -122,6 +122,7 @@ import {
} from '../common/legalDocuments';
import { PlatformActionButton } from '../common/PlatformActionButton';
import { PlatformEmptyState } from '../common/PlatformEmptyState';
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
import { PlatformIconButton } from '../common/PlatformIconButton';
import { PlatformModalCloseButton } from '../common/PlatformModalCloseButton';
import { PlatformPillBadge } from '../common/PlatformPillBadge';
@@ -4070,9 +4071,13 @@ function ProfileSaveArchivesModal({
))}
</div>
) : (
<div className="mt-5 rounded-xl bg-zinc-50 px-4 py-5 text-center text-sm font-semibold text-zinc-500">
<PlatformEmptyState
surface="subpanel"
size="inline"
className="mt-5"
>
</div>
</PlatformEmptyState>
)}
</div>
</div>
@@ -4153,9 +4158,9 @@ function ProfilePlayedWorksModal({
<div className="mt-5 space-y-5">
{hasArchiveEntries ? (
<section>
<div className="mb-2 text-xs font-black text-zinc-500">
<PlatformFieldLabel variant="section" className="mb-2 block">
</div>
</PlatformFieldLabel>
<div className="grid gap-3">
{saveEntries.map((entry) => (
<SaveArchiveCard
@@ -4171,16 +4176,21 @@ function ProfilePlayedWorksModal({
{hasPlayedWorks ? (
<section>
<div className="mb-2 text-xs font-black text-zinc-500">
<PlatformFieldLabel variant="section" className="mb-2 block">
</div>
</PlatformFieldLabel>
<div className="space-y-3">
{playedWorks.map((work) => (
<button
<PlatformSubpanel
as="button"
type="button"
key={`${work.worldKey}:${work.lastPlayedAt}`}
onClick={() => onOpenWork?.(work)}
className="w-full rounded-2xl border border-zinc-200 bg-zinc-50 px-4 py-3 text-left transition hover:border-[#ff4056] hover:bg-white"
surface="flat"
radius="sm"
padding="md"
interactive
className="w-full hover:border-[#ff4056]"
>
<div className="flex min-w-0 items-start justify-between gap-3">
<div className="min-w-0">
@@ -4213,16 +4223,21 @@ function ProfilePlayedWorksModal({
{formatCompactPlayTime(work.lastObservedPlayTimeMs)}
</span>
</div>
</button>
</PlatformSubpanel>
))}
</div>
</section>
) : null}
</div>
) : (
<div className="mt-5 rounded-xl border border-zinc-200 bg-zinc-50 px-4 py-4 text-sm text-zinc-600">
<PlatformEmptyState
surface="subpanel"
size="inline"
tone="base"
className="mt-5 text-left"
>
</div>
</PlatformEmptyState>
)}
</div>
</div>