收口危险操作确认弹窗
新增 PlatformDangerConfirmDialog 统一承接危险确认壳层 迁移平台入口删除作品确认复用共享危险确认弹窗 迁移 RPG 结果页重新生成确认复用共享危险确认弹窗 补充共享组件测试并更新 PlatformUiKit 收口文档与决策记录
This commit is contained in:
68
src/components/common/PlatformDangerConfirmDialog.test.tsx
Normal file
68
src/components/common/PlatformDangerConfirmDialog.test.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import { expect, test, vi } from 'vitest';
|
||||
|
||||
import { PlatformDangerConfirmDialog } from './PlatformDangerConfirmDialog';
|
||||
|
||||
test('renders a standard danger confirmation with cancel and confirm actions', () => {
|
||||
const onClose = vi.fn();
|
||||
const onConfirm = vi.fn();
|
||||
|
||||
render(
|
||||
<PlatformDangerConfirmDialog
|
||||
open
|
||||
title="删除作品"
|
||||
description="确认删除《潮雾列岛》吗?"
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
confirmLabel="确认删除"
|
||||
>
|
||||
删除后不可恢复。
|
||||
</PlatformDangerConfirmDialog>,
|
||||
);
|
||||
|
||||
const dialog = screen.getByRole('dialog', { name: '删除作品' });
|
||||
|
||||
expect(within(dialog).getByText('确认删除《潮雾列岛》吗?')).toBeTruthy();
|
||||
expect(within(dialog).getByText('删除后不可恢复。')).toBeTruthy();
|
||||
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '取消' }));
|
||||
expect(onClose).toHaveBeenCalledTimes(1);
|
||||
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '确认删除' }));
|
||||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('forwards busy state and custom busy label for destructive actions', () => {
|
||||
const onClose = vi.fn();
|
||||
const onConfirm = vi.fn();
|
||||
|
||||
render(
|
||||
<PlatformDangerConfirmDialog
|
||||
open
|
||||
title="删除作品"
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
confirmLabel="确认删除"
|
||||
busyConfirmLabel="删除中"
|
||||
busy
|
||||
closeOnBackdrop={false}
|
||||
>
|
||||
正在删除。
|
||||
</PlatformDangerConfirmDialog>,
|
||||
);
|
||||
|
||||
const dialog = screen.getByRole('dialog', { name: '删除作品' });
|
||||
const confirmButton = within(dialog).getByRole('button', { name: '删除中' });
|
||||
const cancelButton = within(dialog).getByRole('button', { name: '取消' });
|
||||
|
||||
expect((confirmButton as HTMLButtonElement).disabled).toBe(true);
|
||||
expect((cancelButton as HTMLButtonElement).disabled).toBe(true);
|
||||
|
||||
fireEvent.click(confirmButton);
|
||||
fireEvent.click(cancelButton);
|
||||
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
78
src/components/common/PlatformDangerConfirmDialog.tsx
Normal file
78
src/components/common/PlatformDangerConfirmDialog.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { UnifiedConfirmDialog } from './UnifiedConfirmDialog';
|
||||
|
||||
type PlatformDangerConfirmDialogProps = {
|
||||
open: boolean;
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
onConfirm?: () => void;
|
||||
description?: ReactNode;
|
||||
children?: ReactNode;
|
||||
confirmLabel?: string;
|
||||
busy?: boolean;
|
||||
busyConfirmLabel?: string;
|
||||
cancelLabel?: string;
|
||||
closeOnBackdrop?: boolean;
|
||||
showCloseButton?: boolean;
|
||||
portal?: boolean;
|
||||
size?: 'sm' | 'md';
|
||||
variant?: 'platform' | 'pixel';
|
||||
overlayClassName?: string;
|
||||
panelClassName?: string;
|
||||
footerClassName?: string;
|
||||
confirmClassName?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* 平台危险确认弹窗。
|
||||
* 统一承接需要“确认 / 取消 + 危险主动作”语义的标准弹窗壳层。
|
||||
*/
|
||||
export function PlatformDangerConfirmDialog({
|
||||
open,
|
||||
title,
|
||||
onClose,
|
||||
onConfirm,
|
||||
description,
|
||||
children,
|
||||
confirmLabel = '确认',
|
||||
busy = false,
|
||||
busyConfirmLabel,
|
||||
cancelLabel = '取消',
|
||||
closeOnBackdrop = true,
|
||||
showCloseButton = true,
|
||||
portal = true,
|
||||
size = 'sm',
|
||||
variant = 'platform',
|
||||
overlayClassName,
|
||||
panelClassName,
|
||||
footerClassName,
|
||||
confirmClassName,
|
||||
}: PlatformDangerConfirmDialogProps) {
|
||||
return (
|
||||
<UnifiedConfirmDialog
|
||||
open={open}
|
||||
title={title}
|
||||
description={description}
|
||||
onClose={onClose}
|
||||
onConfirm={onConfirm}
|
||||
confirmLabel={confirmLabel}
|
||||
busy={busy}
|
||||
busyConfirmLabel={busyConfirmLabel}
|
||||
cancelLabel={cancelLabel}
|
||||
closeOnBackdrop={closeOnBackdrop}
|
||||
showCloseButton={showCloseButton}
|
||||
showCancel
|
||||
confirmTone="danger"
|
||||
portal={portal}
|
||||
size={size}
|
||||
variant={variant}
|
||||
overlayClassName={overlayClassName}
|
||||
panelClassName={panelClassName}
|
||||
footerClassName={footerClassName}
|
||||
confirmClassName={confirmClassName}
|
||||
>
|
||||
{children}
|
||||
</UnifiedConfirmDialog>
|
||||
);
|
||||
}
|
||||
@@ -364,13 +364,13 @@ import {
|
||||
import type { CustomWorldProfile } from '../../types';
|
||||
import { useAuthUi } from '../auth/AuthUiContext';
|
||||
import { PlatformActionButton } from '../common/PlatformActionButton';
|
||||
import { PlatformDangerConfirmDialog } from '../common/PlatformDangerConfirmDialog';
|
||||
import { PlatformFieldLabel } from '../common/PlatformFieldLabel';
|
||||
import { PlatformStatusDialog } from '../common/PlatformStatusDialog';
|
||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||
import { PlatformSubpanel } from '../common/PlatformSubpanel';
|
||||
import { PublishShareModal } from '../common/PublishShareModal';
|
||||
import type { PublishShareModalPayload } from '../common/publishShareModalModel';
|
||||
import { UnifiedConfirmDialog } from '../common/UnifiedConfirmDialog';
|
||||
import { UnifiedModal } from '../common/UnifiedModal';
|
||||
import { resolveCreativeAgentTargetSelectionStage } from '../creative-agent/creativeAgentViewModel';
|
||||
import {
|
||||
@@ -17056,7 +17056,7 @@ export function PlatformEntryFlowShellImpl({
|
||||
>
|
||||
{workNotFoundRecoveryDialog?.message}
|
||||
</PlatformStatusDialog>
|
||||
<UnifiedConfirmDialog
|
||||
<PlatformDangerConfirmDialog
|
||||
open={Boolean(pendingDeleteCreationWork)}
|
||||
title="删除作品"
|
||||
description={
|
||||
@@ -17070,14 +17070,12 @@ export function PlatformEntryFlowShellImpl({
|
||||
size="sm"
|
||||
overlayClassName={`platform-theme ${platformThemeClass} !items-center`}
|
||||
panelClassName="platform-remap-surface rounded-[1.75rem]"
|
||||
showCancel
|
||||
confirmLabel="确认删除"
|
||||
busyConfirmLabel="删除中"
|
||||
confirmTone="danger"
|
||||
onConfirm={confirmDeleteCreationWork}
|
||||
>
|
||||
{pendingDeleteCreationWork?.detail}
|
||||
</UnifiedConfirmDialog>
|
||||
</PlatformDangerConfirmDialog>
|
||||
<AnimatePresence>
|
||||
{publicSearchError ? (
|
||||
<motion.div
|
||||
|
||||
@@ -2,9 +2,9 @@ import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { rpgCreationAssetClient } from '../../services/rpg-creation/rpgCreationAssetClient';
|
||||
import type { Character, CustomWorldProfile } from '../../types';
|
||||
import { PlatformDangerConfirmDialog } from '../common/PlatformDangerConfirmDialog';
|
||||
import { PlatformProgressBar } from '../common/PlatformProgressBar';
|
||||
import { PlatformStatusMessage } from '../common/PlatformStatusMessage';
|
||||
import { UnifiedConfirmDialog } from '../common/UnifiedConfirmDialog';
|
||||
import {
|
||||
CustomWorldEntityCatalog,
|
||||
type ResultTab,
|
||||
@@ -344,17 +344,15 @@ export function RpgCreationResultView({
|
||||
onProfileChange={onProfileChange}
|
||||
/>
|
||||
{regenerateConfirmOpen ? (
|
||||
<UnifiedConfirmDialog
|
||||
<PlatformDangerConfirmDialog
|
||||
open
|
||||
title="重新生成"
|
||||
onClose={() => setRegenerateConfirmOpen(false)}
|
||||
onConfirm={confirmRegenerate}
|
||||
confirmLabel="确认重新生成"
|
||||
confirmTone="danger"
|
||||
showCancel
|
||||
>
|
||||
{`确认重新生成“${profile.name}”吗?重新生成会覆盖当前世界中的所有信息,包括你修改和新增的内容。`}
|
||||
</UnifiedConfirmDialog>
|
||||
</PlatformDangerConfirmDialog>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user