收口个人中心标准弹窗壳层

扩展 UnifiedModal 支持关闭按钮变体与局部交互语义覆写
将昵称修改充值任务与兑换码弹窗迁移到 UnifiedModal
更新 PlatformUiKit 收口计划和 Hermes 决策记录
This commit is contained in:
2026-06-10 17:05:05 +08:00
parent 9a04ea55dc
commit ba5f84d963
5 changed files with 400 additions and 343 deletions

View File

@@ -39,6 +39,30 @@ test('closes through backdrop and escape', () => {
expect(onClose).toHaveBeenCalledTimes(2);
});
test('supports disabling escape close while keeping the custom close button chrome', () => {
const onClose = vi.fn();
render(
<UnifiedModal
open
title="个人中心弹窗"
onClose={onClose}
closeOnEscape={false}
closeVariant="profileCompact"
titleClassName="font-black"
portal={false}
>
<div></div>
</UnifiedModal>,
);
fireEvent.keyDown(window, { key: 'Escape' });
expect(onClose).not.toHaveBeenCalled();
const closeButton = screen.getByRole('button', { name: '关闭' });
expect(closeButton.className).toContain('platform-profile-icon-button');
expect(screen.getByRole('dialog', { name: '个人中心弹窗' })).toBeTruthy();
});
test('respects closeDisabled for every default close path', () => {
const onClose = vi.fn();
render(

View File

@@ -1,4 +1,5 @@
import {
type ComponentProps,
type CSSProperties,
type ReactNode,
useEffect,
@@ -11,6 +12,9 @@ import { PlatformModalCloseButton } from './PlatformModalCloseButton';
type UnifiedModalVariant = 'platform' | 'pixel';
type UnifiedModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'fullscreen';
type UnifiedModalCloseVariant = NonNullable<
ComponentProps<typeof PlatformModalCloseButton>['variant']
>;
type UnifiedModalProps = {
open: boolean;
@@ -23,13 +27,17 @@ type UnifiedModalProps = {
size?: UnifiedModalSize;
closeDisabled?: boolean;
closeOnBackdrop?: boolean;
closeOnEscape?: boolean;
showCloseButton?: boolean;
closeLabel?: string;
closeVariant?: UnifiedModalCloseVariant;
portal?: boolean;
zIndexClassName?: string;
overlayClassName?: string;
panelClassName?: string;
headerClassName?: string;
titleClassName?: string;
descriptionClassName?: string;
bodyClassName?: string;
footerClassName?: string;
panelStyle?: CSSProperties;
@@ -80,12 +88,16 @@ function UnifiedModalContent({
size = 'md',
closeDisabled = false,
closeOnBackdrop = true,
closeOnEscape = true,
showCloseButton = true,
closeLabel = '关闭',
closeVariant,
zIndexClassName = 'z-[90]',
overlayClassName,
panelClassName,
headerClassName,
titleClassName,
descriptionClassName,
bodyClassName,
footerClassName,
panelStyle,
@@ -94,7 +106,7 @@ function UnifiedModalContent({
const descriptionId = useId();
useEffect(() => {
if (!open || closeDisabled) {
if (!open || closeDisabled || !closeOnEscape) {
return;
}
@@ -106,7 +118,7 @@ function UnifiedModalContent({
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [closeDisabled, onClose, open]);
}, [closeDisabled, closeOnEscape, onClose, open]);
if (!open) {
return null;
@@ -169,11 +181,20 @@ function UnifiedModalContent({
>
<div className={joinClassNames(headerClasses, headerClassName)}>
<div className="min-w-0">
<div id={titleId} className={titleClasses}>
<div
id={titleId}
className={joinClassNames(titleClasses, titleClassName)}
>
{title}
</div>
{description ? (
<div id={descriptionId} className={descriptionClasses}>
<div
id={descriptionId}
className={joinClassNames(
descriptionClasses,
descriptionClassName,
)}
>
{description}
</div>
) : null}
@@ -183,7 +204,7 @@ function UnifiedModalContent({
label={closeLabel}
onClick={onClose}
disabled={closeDisabled}
variant={isPixel ? 'pixel' : 'platformIcon'}
variant={closeVariant ?? (isPixel ? 'pixel' : 'platformIcon')}
/>
) : null}
</div>