修复泥点弹窗透明问题
为泥点消耗确认弹窗补齐平台主题作用域和模态面板样式 让平台状态弹窗合并默认主题遮罩,避免自定义遮罩覆盖主题变量 补充弹窗默认样式测试和团队排障记录
This commit is contained in:
@@ -200,6 +200,14 @@
|
|||||||
- 验证:`npm test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "puzzle form checks mud points before creating a draft|match3d form checks mud points before creating a draft|bark battle form checks mud points before creating image assets"` 应断言弹窗出现、对应工作台仍在、玩法模板分类不再出现。
|
- 验证:`npm test -- src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx -t "puzzle form checks mud points before creating a draft|match3d form checks mud points before creating a draft|bark battle form checks mud points before creating image assets"` 应断言弹窗出现、对应工作台仍在、玩法模板分类不再出现。
|
||||||
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
- 关联:`src/components/platform-entry/PlatformEntryFlowShellImpl.tsx`、`src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx`、`docs/【玩法创作】平台入口与玩法链路-2026-05-15.md`。
|
||||||
|
|
||||||
|
## 内嵌泥点确认弹窗必须自带平台主题作用域
|
||||||
|
|
||||||
|
- 现象:拼图 / 抓大鹅统一创作页点击生成后,“确认消耗泥点”弹窗正文和按钮存在,但弹窗面板背景透明,只剩遮罩和文字。
|
||||||
|
- 原因:`PlatformMudPointConfirmDialog` 作为二级确认常以 `portal={false}` 内嵌到工作台局部 DOM,局部节点不一定继承 `.platform-theme`;`platform-modal-shell` 依赖 `--platform-modal-fill` 等主题变量,变量缺失时面板底色解析为空。
|
||||||
|
- 处理:共享泥点确认弹窗默认在 overlay 上带 `platform-theme platform-theme--<theme>`、`platform-modal-backdrop` 和实色遮罩,在 panel 上带 `platform-modal-shell platform-remap-surface`;单按钮状态弹窗也要有默认 light 主题,避免未来独立调用复现。
|
||||||
|
- 验证:浏览器触发 `/creation/puzzle` 与 `/creation/match3d` 的泥点确认弹窗,检查 overlay 最近主题 class 存在、`--platform-modal-fill` 有值且面板为实底;聚焦测试覆盖默认 overlay / panel class。
|
||||||
|
- 关联:`src/components/common/PlatformMudPointConfirmDialog.tsx`、`src/components/common/PlatformStatusDialog.tsx`、`src/components/unified-creation/workspaces/PuzzleCreationWorkspace.tsx`、`src/components/unified-creation/workspaces/Match3DCreationWorkspace.tsx`。
|
||||||
|
|
||||||
## 玩法入口分类字段缺失要前端兜底
|
## 玩法入口分类字段缺失要前端兜底
|
||||||
|
|
||||||
- 现象:平台创作入口初始化时,`platformEntryCreationTypes.ts` 直接对 `creationTypes[].categoryId` / `categoryLabel` 调 `trim()`,一旦后端旧数据、局部 mock 或异常返回里缺字段,整个创作页会在 `derivePlatformCreationTypes(...)` 里直接炸掉。
|
- 现象:平台创作入口初始化时,`platformEntryCreationTypes.ts` 直接对 `creationTypes[].categoryId` / `categoryLabel` 调 `trim()`,一旦后端旧数据、局部 mock 或异常返回里缺字段,整个创作页会在 `derivePlatformCreationTypes(...)` 里直接炸掉。
|
||||||
|
|||||||
@@ -51,3 +51,27 @@ test('supports extra detail copy and close button override', () => {
|
|||||||
expect(within(dialog).getByText('本次会覆盖当前待确认素材。')).toBeTruthy();
|
expect(within(dialog).getByText('本次会覆盖当前待确认素材。')).toBeTruthy();
|
||||||
expect(screen.queryByRole('button', { name: '关闭' })).toBeNull();
|
expect(screen.queryByRole('button', { name: '关闭' })).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('applies the stronger default overlay and panel chrome', () => {
|
||||||
|
render(
|
||||||
|
<PlatformMudPointConfirmDialog
|
||||||
|
open
|
||||||
|
points={10}
|
||||||
|
onClose={() => {}}
|
||||||
|
onConfirm={() => {}}
|
||||||
|
portal={false}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dialog = screen.getByRole('dialog', { name: '确认消耗泥点' });
|
||||||
|
const overlay = dialog.parentElement as HTMLElement;
|
||||||
|
|
||||||
|
expect(overlay.className).toContain('platform-modal-backdrop');
|
||||||
|
expect(overlay.className).toContain('platform-theme--light');
|
||||||
|
expect(overlay.className).toContain('!bg-black/45');
|
||||||
|
expect(dialog.className).toContain('platform-modal-shell');
|
||||||
|
expect(dialog.className).toContain('max-w-xs');
|
||||||
|
expect(dialog.className).toContain(
|
||||||
|
'shadow-[0_24px_70px_rgba(15,23,42,0.22)]',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
|
import { useAuthUi } from '../auth/AuthUiContext';
|
||||||
import { UnifiedConfirmDialog } from './UnifiedConfirmDialog';
|
import { UnifiedConfirmDialog } from './UnifiedConfirmDialog';
|
||||||
|
|
||||||
type PlatformMudPointConfirmDialogProps = {
|
type PlatformMudPointConfirmDialogProps = {
|
||||||
@@ -19,6 +20,14 @@ type PlatformMudPointConfirmDialogProps = {
|
|||||||
panelClassName?: string;
|
panelClassName?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const DEFAULT_OVERLAY_CLASS_NAME = 'platform-modal-backdrop z-[80] !bg-black/45';
|
||||||
|
const DEFAULT_PANEL_CLASS_NAME =
|
||||||
|
'platform-modal-shell platform-remap-surface max-w-xs rounded-[1.35rem] shadow-[0_24px_70px_rgba(15,23,42,0.22)]';
|
||||||
|
|
||||||
|
function joinClassNames(...classNames: Array<string | undefined>) {
|
||||||
|
return classNames.filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 平台泥点消耗确认弹窗。
|
* 平台泥点消耗确认弹窗。
|
||||||
* 统一承接“确认消耗泥点 + 消耗 N 泥点”的标准确认壳层,业务侧只保留点数与确认动作。
|
* 统一承接“确认消耗泥点 + 消耗 N 泥点”的标准确认壳层,业务侧只保留点数与确认动作。
|
||||||
@@ -39,6 +48,8 @@ export function PlatformMudPointConfirmDialog({
|
|||||||
overlayClassName,
|
overlayClassName,
|
||||||
panelClassName,
|
panelClassName,
|
||||||
}: PlatformMudPointConfirmDialogProps) {
|
}: PlatformMudPointConfirmDialogProps) {
|
||||||
|
const resolvedPlatformTheme = useAuthUi()?.platformTheme ?? 'light';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UnifiedConfirmDialog
|
<UnifiedConfirmDialog
|
||||||
open={open}
|
open={open}
|
||||||
@@ -52,8 +63,12 @@ export function PlatformMudPointConfirmDialog({
|
|||||||
showCloseButton={showCloseButton}
|
showCloseButton={showCloseButton}
|
||||||
portal={portal}
|
portal={portal}
|
||||||
size={size}
|
size={size}
|
||||||
overlayClassName={overlayClassName}
|
overlayClassName={joinClassNames(
|
||||||
panelClassName={panelClassName}
|
`platform-theme platform-theme--${resolvedPlatformTheme}`,
|
||||||
|
DEFAULT_OVERLAY_CLASS_NAME,
|
||||||
|
overlayClassName,
|
||||||
|
)}
|
||||||
|
panelClassName={joinClassNames(DEFAULT_PANEL_CLASS_NAME, panelClassName)}
|
||||||
>
|
>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="font-semibold">消耗 {points} 泥点</div>
|
<div className="font-semibold">消耗 {points} 泥点</div>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ test('renders result state with description and primary action', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const dialog = screen.getByRole('dialog', { name: '支付成功' });
|
const dialog = screen.getByRole('dialog', { name: '支付成功' });
|
||||||
|
const overlay = dialog.parentElement as HTMLElement;
|
||||||
const badge = dialog.querySelector('.platform-icon-badge');
|
const badge = dialog.querySelector('.platform-icon-badge');
|
||||||
const action = screen.getByRole('button', { name: '知道了' });
|
const action = screen.getByRole('button', { name: '知道了' });
|
||||||
const visibleDescription = dialog.querySelector(
|
const visibleDescription = dialog.querySelector(
|
||||||
@@ -28,6 +29,8 @@ test('renders result state with description and primary action', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(dialog).toBeTruthy();
|
expect(dialog).toBeTruthy();
|
||||||
|
expect(overlay.className).toContain('platform-theme--light');
|
||||||
|
expect(overlay.className).toContain('platform-profile-modal-overlay');
|
||||||
expect(visibleDescription?.textContent).toBe('账户状态已刷新');
|
expect(visibleDescription?.textContent).toBe('账户状态已刷新');
|
||||||
expect(badge?.className).toContain('text-[var(--platform-success-text)]');
|
expect(badge?.className).toContain('text-[var(--platform-success-text)]');
|
||||||
expect(action.className).toContain('platform-primary-button');
|
expect(action.className).toContain('platform-primary-button');
|
||||||
@@ -97,6 +100,27 @@ test('supports custom badge icon label and action button styling', () => {
|
|||||||
expect(action.className).toContain('bg-slate-950');
|
expect(action.className).toContain('bg-slate-950');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('keeps default theme classes when callers customize the overlay', () => {
|
||||||
|
render(
|
||||||
|
<PlatformStatusDialog
|
||||||
|
status="error"
|
||||||
|
title="发布失败"
|
||||||
|
description="图片生成失败"
|
||||||
|
onClose={vi.fn()}
|
||||||
|
overlayClassName="bg-slate-950/58 custom-overlay"
|
||||||
|
action={{ label: '知道了', onClick: vi.fn(), surface: 'platform' }}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dialog = screen.getByRole('dialog', { name: '发布失败' });
|
||||||
|
const overlay = dialog.parentElement as HTMLElement;
|
||||||
|
|
||||||
|
expect(overlay.className).toContain('platform-theme--light');
|
||||||
|
expect(overlay.className).toContain('platform-profile-modal-overlay');
|
||||||
|
expect(overlay.className).toContain('bg-slate-950/58');
|
||||||
|
expect(overlay.className).toContain('custom-overlay');
|
||||||
|
});
|
||||||
|
|
||||||
test('supports header notice layout with body content and close button', () => {
|
test('supports header notice layout with body content and close button', () => {
|
||||||
const onClose = vi.fn();
|
const onClose = vi.fn();
|
||||||
|
|
||||||
|
|||||||
@@ -64,11 +64,15 @@ type PlatformStatusVisualConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_OVERLAY_CLASS =
|
const DEFAULT_OVERLAY_CLASS =
|
||||||
'platform-profile-modal-overlay bg-slate-950/72 backdrop-blur-xl';
|
'platform-theme platform-theme--light platform-profile-modal-overlay bg-slate-950/72 backdrop-blur-xl';
|
||||||
const DEFAULT_PANEL_CLASS =
|
const DEFAULT_PANEL_CLASS =
|
||||||
'platform-remap-surface !max-w-sm rounded-[1.4rem]';
|
'platform-remap-surface !max-w-sm rounded-[1.4rem]';
|
||||||
const DEFAULT_BODY_CLASS = 'px-5 pb-5 pt-6 text-center';
|
const DEFAULT_BODY_CLASS = 'px-5 pb-5 pt-6 text-center';
|
||||||
|
|
||||||
|
function joinClassNames(...classNames: Array<string | undefined>) {
|
||||||
|
return classNames.filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
function getStatusVisualConfig(
|
function getStatusVisualConfig(
|
||||||
status: PlatformStatusDialogStatus,
|
status: PlatformStatusDialogStatus,
|
||||||
): PlatformStatusVisualConfig {
|
): PlatformStatusVisualConfig {
|
||||||
@@ -119,7 +123,7 @@ export function PlatformStatusDialog({
|
|||||||
closeLabel,
|
closeLabel,
|
||||||
closeDisabled = false,
|
closeDisabled = false,
|
||||||
zIndexClassName = 'z-[90]',
|
zIndexClassName = 'z-[90]',
|
||||||
overlayClassName = DEFAULT_OVERLAY_CLASS,
|
overlayClassName,
|
||||||
panelClassName = DEFAULT_PANEL_CLASS,
|
panelClassName = DEFAULT_PANEL_CLASS,
|
||||||
bodyClassName = DEFAULT_BODY_CLASS,
|
bodyClassName = DEFAULT_BODY_CLASS,
|
||||||
iconClassName,
|
iconClassName,
|
||||||
@@ -144,7 +148,10 @@ export function PlatformStatusDialog({
|
|||||||
portal={false}
|
portal={false}
|
||||||
size="sm"
|
size="sm"
|
||||||
zIndexClassName={zIndexClassName}
|
zIndexClassName={zIndexClassName}
|
||||||
overlayClassName={overlayClassName}
|
overlayClassName={joinClassNames(
|
||||||
|
DEFAULT_OVERLAY_CLASS,
|
||||||
|
overlayClassName,
|
||||||
|
)}
|
||||||
panelClassName={panelClassName}
|
panelClassName={panelClassName}
|
||||||
bodyClassName={bodyClassName}
|
bodyClassName={bodyClassName}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user