继续收口首页导航行与暗色弹窗底栏

将首页排行行与分类行切换为 PlatformNavigableListItem
将 NPC 交易详情与地图切场确认底栏收口到 PlatformDarkModalFooter
补充首页与弹窗回归测试并更新 PlatformUiKit 收口计划与共享决策记录
This commit is contained in:
2026-06-11 05:06:07 +08:00
parent ce13bdbb02
commit 193e4f0e96
8 changed files with 111 additions and 83 deletions

View File

@@ -82,6 +82,7 @@ test('目标场景确认面板复用暗色琥珀 PlatformSubpanel 和胶囊标
const panel = screen.getByTestId('map-target-scene-panel');
const currentSummary = screen.getByTestId('map-current-scene-summary');
const nextSummary = screen.getByTestId('map-next-scene-summary');
const footer = screen.getByTestId('map-travel-footer');
const labelBadge = within(panel).getByText(destinationLabel);
const cancelButton = screen.getByRole('button', { name: '取消' });
const confirmButton = screen.getByRole('button', { name: '确认前往' });
@@ -96,6 +97,8 @@ test('目标场景确认面板复用暗色琥珀 PlatformSubpanel 和胶囊标
expect(currentSummary.className).toContain('bg-black/25');
expect(nextSummary.className).toContain('border-white/10');
expect(nextSummary.className).toContain('bg-black/25');
expect(footer.className).toContain('platform-dark-modal-footer');
expect(footer.className).toContain('border-t');
expect(cancelButton.className).toContain(
'platform-action-button--editor-dark',
);

View File

@@ -7,6 +7,7 @@ import { useResolvedAssetReadUrl } from '../hooks/useResolvedAssetReadUrl';
import { ScenePresetInfo, WorldType } from '../types';
import { CHROME_ICONS, getNineSliceStyle, UI_CHROME } from '../uiAssets';
import { PlatformActionButton } from './common/PlatformActionButton';
import { PlatformDarkModalFooter } from './common/PlatformDarkModalFooter';
import { PlatformPillBadge } from './common/PlatformPillBadge';
import { PlatformSubpanel } from './common/PlatformSubpanel';
import { PixelCloseButton } from './PixelCloseButton';
@@ -441,26 +442,26 @@ export function MapModal({
</PlatformSubpanel>
</div>
<div className="flex justify-end gap-2">
<PlatformActionButton
surface="editorDark"
tone="ghost"
size="xs"
onClick={() => setPendingScene(null)}
>
</PlatformActionButton>
<PlatformActionButton
surface="editorDark"
tone={isTraveling || !canTravel ? 'ghost' : 'warning'}
size="xs"
disabled={isTraveling || !canTravel}
onClick={confirmTravel}
>
{isTraveling ? '切换中...' : canTravel ? '确认前往' : '当前不可切换'}
</PlatformActionButton>
</div>
</div>
<PlatformDarkModalFooter data-testid="map-travel-footer">
<PlatformActionButton
surface="editorDark"
tone="ghost"
size="xs"
onClick={() => setPendingScene(null)}
>
</PlatformActionButton>
<PlatformActionButton
surface="editorDark"
tone={isTraveling || !canTravel ? 'ghost' : 'warning'}
size="xs"
disabled={isTraveling || !canTravel}
onClick={confirmTravel}
>
{isTraveling ? '切换中...' : canTravel ? '确认前往' : '当前不可切换'}
</PlatformActionButton>
</PlatformDarkModalFooter>
</motion.div>
</motion.div>
)}

View File

@@ -311,8 +311,12 @@ test('NPC 弹窗标准 dark footer CTA 复用 PlatformActionButton', async () =>
await user.click(screen.getByRole('button', { name: /月壳/ }));
const tradeDetailFooter = screen.getByTestId('npc-trade-detail-footer');
const closeButton = screen.getByRole('button', { name: '关闭' });
expect(tradeDetailFooter.className).toContain('platform-dark-modal-footer');
expect(tradeDetailFooter.className).toContain('px-5');
expect(tradeDetailFooter.className).toContain('py-4');
expect(closeButton.className).toContain('platform-action-button--editor-dark');
expect(closeButton.className).toContain('rounded-2xl');
});

View File

@@ -560,17 +560,21 @@ export function NpcModals({ gameState, npcUi }: NpcModalsProps) {
</PlatformStatusMessage>
)}
<div className="flex justify-end">
<PlatformActionButton
surface="editorDark"
tone="secondary"
size="xs"
onClick={() => setTradeDetail(null)}
>
</PlatformActionButton>
</div>
</div>
<PlatformDarkModalFooter
padding="roomy"
data-testid="npc-trade-detail-footer"
>
<PlatformActionButton
surface="editorDark"
tone="secondary"
size="xs"
onClick={() => setTradeDetail(null)}
>
</PlatformActionButton>
</PlatformDarkModalFooter>
</motion.div>
</motion.div>
)}

View File

@@ -5416,7 +5416,11 @@ test('mobile home moves category shelf into game category channel', async () =>
expect(screen.getAllByText('分类').length).toBeGreaterThan(0);
expect(screen.getByRole('button', { name: //u })).toBeTruthy();
expect(screen.getByRole('button', { name: '奇幻' })).toBeTruthy();
expect(screen.getByRole('button', { name: //u })).toBeTruthy();
const categoryEntryButton = screen.getByRole('button', {
name: //u,
});
expect(categoryEntryButton).toBeTruthy();
expect(categoryEntryButton.className).toContain('platform-navigable-list-item');
expect(container.querySelector('.platform-category-game-list')).toBeTruthy();
expect(container.querySelector('.platform-category-game-item')).toBeTruthy();
expect(
@@ -5590,4 +5594,8 @@ test('ranking rows limit displayed work name and show two short tags on the thir
expect(within(rankingPanel!).getByText('古风机关')).toBeTruthy();
expect(within(rankingPanel!).queryByText(/2026-04-29/u)).toBeNull();
expect(within(rankingPanel!).queryByText('拼图玩家')).toBeNull();
expect(
within(rankingPanel!).getByRole('button', { name: //u })
.className,
).toContain('platform-navigable-list-item');
});

View File

@@ -2003,24 +2003,32 @@ function PlatformRankingItem({
: '进入';
return (
<button
type="button"
<PlatformNavigableListItem
onClick={onClick}
className="platform-ranking-item w-full text-left"
align="center"
className="platform-ranking-item"
leadingClassName="flex items-center gap-3"
leading={
<>
<div className="platform-ranking-item__rank">{rank}</div>
<div className="platform-ranking-item__cover">
{coverImage ? (
<PlatformWorkCoverArtwork
entry={entry}
imageSrc={coverImage}
alt={entry.worldName}
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-[radial-gradient(circle_at_24%_18%,rgba(255,255,255,0.24),transparent_30%),linear-gradient(135deg,rgba(255,118,117,0.42),rgba(89,164,255,0.34))]" />
)}
</div>
</>
}
trailing={
<span className="platform-ranking-item__action">{actionLabel}</span>
}
>
<div className="platform-ranking-item__rank">{rank}</div>
<div className="platform-ranking-item__cover">
{coverImage ? (
<PlatformWorkCoverArtwork
entry={entry}
imageSrc={coverImage}
alt={entry.worldName}
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-[radial-gradient(circle_at_24%_18%,rgba(255,255,255,0.24),transparent_30%),linear-gradient(135deg,rgba(255,118,117,0.42),rgba(89,164,255,0.34))]" />
)}
</div>
<div className="min-w-0 flex-1">
<div className="line-clamp-1 break-words text-base font-black leading-tight text-[var(--platform-text-strong)]">
{displayName}
@@ -2041,8 +2049,7 @@ function PlatformRankingItem({
))}
</div>
</div>
<span className="platform-ranking-item__action">{actionLabel}</span>
</button>
</PlatformNavigableListItem>
);
}
@@ -2071,49 +2078,46 @@ function PlatformCategoryGameItem({
entry.summaryText || entry.subtitle || `${displayName} 正在等待摘要。`;
return (
<button
type="button"
<PlatformNavigableListItem
onClick={onClick}
aria-label={`${entry.worldName}${actionLabel}`}
align="center"
className="platform-category-game-item"
bodyClassName="platform-category-game-item__body"
leading={
<div className="platform-category-game-item__cover">
{coverImage ? (
<PlatformWorkCoverArtwork
entry={entry}
imageSrc={coverImage}
alt={entry.worldName}
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-[radial-gradient(circle_at_24%_18%,rgba(255,255,255,0.26),transparent_32%),linear-gradient(135deg,rgba(255,118,117,0.44),rgba(89,164,255,0.36))]" />
)}
</div>
}
trailing={
<span className="platform-category-game-item__action">{actionLabel}</span>
}
>
<div className="platform-category-game-item__cover">
{coverImage ? (
<PlatformWorkCoverArtwork
entry={entry}
imageSrc={coverImage}
alt={entry.worldName}
className="h-full w-full object-cover"
/>
) : (
<div className="h-full w-full bg-[radial-gradient(circle_at_24%_18%,rgba(255,255,255,0.26),transparent_32%),linear-gradient(135deg,rgba(255,118,117,0.44),rgba(89,164,255,0.36))]" />
)}
<div className="platform-category-game-item__title-row">
<span className="platform-category-game-item__title">{displayName}</span>
<span className="platform-category-game-item__badge"></span>
</div>
<div className="platform-category-game-item__body">
<div className="platform-category-game-item__title-row">
<span className="platform-category-game-item__title">
{displayName}
</span>
<span className="platform-category-game-item__badge"></span>
</div>
<div className="platform-category-game-item__meta">
<span className="platform-category-game-item__metric">
<Star className="h-3.5 w-3.5 fill-current" />
<span>{formatPlatformCompactCount(metric.value)}</span>
</span>
<span>{metric.label}</span>
{metaParts.length > 0 ? <span>{metaParts.join(' · ')}</span> : null}
</div>
<div className="platform-category-game-item__summary">
{summaryText}
</div>
<div className="platform-category-game-item__meta">
<span className="platform-category-game-item__metric">
<Star className="h-3.5 w-3.5 fill-current" />
<span>{formatPlatformCompactCount(metric.value)}</span>
</span>
<span>{metric.label}</span>
{metaParts.length > 0 ? <span>{metaParts.join(' · ')}</span> : null}
</div>
<span className="platform-category-game-item__action">{actionLabel}</span>
</button>
<div className="platform-category-game-item__summary">{summaryText}</div>
</PlatformNavigableListItem>
);
}