收口分类筛选与扫码弹窗壳层
RpgEntryHomeView 的分类筛选弹窗和扫码面板改用 UnifiedModal 补充扫码关闭清理与分类筛选关闭路径测试 更新 PlatformUiKit 收口计划与 .hermes 决策记录
This commit is contained in:
@@ -3086,7 +3086,12 @@ test('profile scan action opens camera scanner instead of recharge panel', async
|
||||
within(topbar as HTMLElement).getByRole('button', { name: '扫码' }),
|
||||
);
|
||||
|
||||
expect(await screen.findByRole('dialog', { name: '扫码' })).toBeTruthy();
|
||||
const qrScannerDialog = await screen.findByRole('dialog', { name: '扫码' });
|
||||
|
||||
expect(qrScannerDialog).toBeTruthy();
|
||||
expect(
|
||||
within(qrScannerDialog).getByRole('button', { name: '关闭扫码' }),
|
||||
).toBeTruthy();
|
||||
await waitFor(() => {
|
||||
expect(getUserMedia).toHaveBeenCalledWith({
|
||||
audio: false,
|
||||
@@ -3094,6 +3099,14 @@ test('profile scan action opens camera scanner instead of recharge panel', async
|
||||
});
|
||||
});
|
||||
expect(mockGetRpgProfileRechargeCenter).not.toHaveBeenCalled();
|
||||
expect(screen.queryByText('账户充值')).toBeNull();
|
||||
|
||||
await user.click(within(qrScannerDialog).getByRole('button', { name: '关闭扫码' }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog', { name: '扫码' })).toBeNull();
|
||||
});
|
||||
expect(stopTrack).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('desktop account entry uses saved avatar image when available', async () => {
|
||||
@@ -5294,11 +5307,21 @@ test('mobile game category filter dialog filters by play type', async () => {
|
||||
const filterDialog = await screen.findByRole('dialog', {
|
||||
name: '分类筛选',
|
||||
});
|
||||
const closeButton = within(filterDialog).getByRole('button', {
|
||||
name: '关闭分类筛选',
|
||||
});
|
||||
|
||||
expect(closeButton.className).toContain('platform-icon-button');
|
||||
|
||||
await user.click(within(filterDialog).getByRole('button', { name: '抓鹅' }));
|
||||
|
||||
expect(screen.queryByRole('button', { name: /奇幻拼图,试玩/u })).toBeNull();
|
||||
expect(screen.getByRole('button', { name: /奇幻抓鹅,进入/u })).toBeTruthy();
|
||||
|
||||
await user.click(closeButton);
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog', { name: '分类筛选' })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
test('bottom category tab becomes ranking and switches ranking metrics', async () => {
|
||||
|
||||
@@ -2326,99 +2326,85 @@ function PlatformCategoryFilterDialog({
|
||||
onClose: () => void;
|
||||
}) {
|
||||
return (
|
||||
<div className="platform-modal-backdrop fixed inset-0 z-[90] flex items-end justify-center px-3 py-4 sm:items-center">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="关闭分类筛选"
|
||||
className="absolute inset-0"
|
||||
onClick={onClose}
|
||||
/>
|
||||
<div
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="分类筛选"
|
||||
className="platform-modal-shell platform-category-filter-dialog relative w-full max-w-md overflow-hidden rounded-[1.35rem]"
|
||||
>
|
||||
<div className="flex min-w-0 items-center justify-between gap-3 px-4 py-4">
|
||||
<div className="min-w-0">
|
||||
<div className="text-base font-black text-[var(--platform-text-strong)]">
|
||||
分类筛选
|
||||
</div>
|
||||
<div className="mt-0.5 text-xs font-semibold text-[var(--platform-text-soft)]">
|
||||
{resultCount} 个作品
|
||||
</div>
|
||||
</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭"
|
||||
onClick={onClose}
|
||||
icon={<XCircle className="h-5 w-5" />}
|
||||
/>
|
||||
<UnifiedModal
|
||||
open
|
||||
title="分类筛选"
|
||||
description={`${resultCount} 个作品`}
|
||||
onClose={onClose}
|
||||
closeOnBackdrop
|
||||
closeOnEscape={false}
|
||||
closeLabel="关闭分类筛选"
|
||||
portal={false}
|
||||
size="sm"
|
||||
zIndexClassName="z-[90]"
|
||||
overlayClassName="platform-modal-backdrop !px-3 !py-4"
|
||||
panelClassName="platform-category-filter-dialog relative !rounded-[1.35rem]"
|
||||
headerClassName="border-b-0 px-4 py-4"
|
||||
titleClassName="text-base font-black"
|
||||
descriptionClassName="mt-0.5 text-xs font-semibold text-[var(--platform-text-soft)]"
|
||||
bodyClassName="grid gap-4 !px-4 !pb-4 !pt-0 sm:!px-4 sm:!pb-4 sm:!pt-0"
|
||||
>
|
||||
<div className="grid gap-2">
|
||||
<div className="text-xs font-black text-[var(--platform-text-soft)]">
|
||||
玩法
|
||||
</div>
|
||||
<div className="platform-category-filter-dialog__options">
|
||||
{PLATFORM_CATEGORY_KIND_FILTERS.map((option) => {
|
||||
const active = option.id === kindFilter;
|
||||
|
||||
<div className="grid gap-4 px-4 pb-4">
|
||||
<div className="grid gap-2">
|
||||
<div className="text-xs font-black text-[var(--platform-text-soft)]">
|
||||
玩法
|
||||
</div>
|
||||
<div className="platform-category-filter-dialog__options">
|
||||
{PLATFORM_CATEGORY_KIND_FILTERS.map((option) => {
|
||||
const active = option.id === kindFilter;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => onKindFilterChange(option.id)}
|
||||
className={`platform-category-filter-dialog__option ${active ? 'platform-category-filter-dialog__option--active' : ''}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="text-xs font-black text-[var(--platform-text-soft)]">
|
||||
排序
|
||||
</div>
|
||||
<div className="platform-category-filter-dialog__options">
|
||||
{PLATFORM_CATEGORY_SORT_OPTIONS.map((option) => {
|
||||
const active = option.id === sortMode;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => onSortModeChange(option.id)}
|
||||
className={`platform-category-filter-dialog__option ${active ? 'platform-category-filter-dialog__option--active' : ''}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="platform-category-filter-dialog__actions">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onReset}
|
||||
className="platform-category-filter-dialog__action platform-category-filter-dialog__action--secondary"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="platform-category-filter-dialog__action platform-category-filter-dialog__action--primary"
|
||||
>
|
||||
完成
|
||||
</button>
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => onKindFilterChange(option.id)}
|
||||
className={`platform-category-filter-dialog__option ${active ? 'platform-category-filter-dialog__option--active' : ''}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="text-xs font-black text-[var(--platform-text-soft)]">
|
||||
排序
|
||||
</div>
|
||||
<div className="platform-category-filter-dialog__options">
|
||||
{PLATFORM_CATEGORY_SORT_OPTIONS.map((option) => {
|
||||
const active = option.id === sortMode;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => onSortModeChange(option.id)}
|
||||
className={`platform-category-filter-dialog__option ${active ? 'platform-category-filter-dialog__option--active' : ''}`}
|
||||
>
|
||||
{option.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="platform-category-filter-dialog__actions">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onReset}
|
||||
className="platform-category-filter-dialog__action platform-category-filter-dialog__action--secondary"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="platform-category-filter-dialog__action platform-category-filter-dialog__action--primary"
|
||||
>
|
||||
完成
|
||||
</button>
|
||||
</div>
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3866,53 +3852,60 @@ function ProfileQrScannerModal({
|
||||
}, [onError, onResult]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="platform-modal-backdrop fixed inset-0 z-[80] flex items-center justify-center px-4 py-6"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="扫码"
|
||||
<UnifiedModal
|
||||
open
|
||||
title="扫码"
|
||||
onClose={onClose}
|
||||
showHeader={false}
|
||||
showCloseButton={false}
|
||||
closeOnBackdrop={false}
|
||||
closeOnEscape={false}
|
||||
portal={false}
|
||||
size="sm"
|
||||
zIndexClassName="z-[80]"
|
||||
overlayClassName={PROFILE_MODAL_OVERLAY_CLASS}
|
||||
panelClassName="platform-qr-scanner-modal !max-w-sm rounded-[1.4rem]"
|
||||
bodyClassName="!p-0"
|
||||
>
|
||||
<div className="platform-qr-scanner-modal w-full max-w-sm overflow-hidden rounded-[1.4rem]">
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div className="text-base font-black">扫码</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭扫码"
|
||||
onClick={onClose}
|
||||
icon="×"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-3 px-5 py-5">
|
||||
<div className="platform-qr-scanner-modal__viewport">
|
||||
<video
|
||||
ref={videoRef}
|
||||
className="h-full w-full object-cover"
|
||||
playsInline
|
||||
muted
|
||||
/>
|
||||
<span className="platform-qr-scanner-modal__frame" />
|
||||
</div>
|
||||
{result ? (
|
||||
<PlatformStatusMessage
|
||||
tone="success"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
已识别:{result}
|
||||
</PlatformStatusMessage>
|
||||
) : error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
|
||||
<div className="text-base font-black">扫码</div>
|
||||
<PlatformModalCloseButton
|
||||
label="关闭扫码"
|
||||
onClick={onClose}
|
||||
icon="×"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-3 px-5 py-5">
|
||||
<div className="platform-qr-scanner-modal__viewport">
|
||||
<video
|
||||
ref={videoRef}
|
||||
className="h-full w-full object-cover"
|
||||
playsInline
|
||||
muted
|
||||
/>
|
||||
<span className="platform-qr-scanner-modal__frame" />
|
||||
</div>
|
||||
{result ? (
|
||||
<PlatformStatusMessage
|
||||
tone="success"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
已识别:{result}
|
||||
</PlatformStatusMessage>
|
||||
) : error ? (
|
||||
<PlatformStatusMessage
|
||||
tone="error"
|
||||
surface="profile"
|
||||
size="xs"
|
||||
className="rounded-2xl font-semibold"
|
||||
>
|
||||
{error}
|
||||
</PlatformStatusMessage>
|
||||
) : null}
|
||||
</div>
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user