收口平台报告弹窗
新增 PlatformReportDialog 公共可复制报告弹窗组件 将 PlatformErrorDialog 与 PlatformTaskCompletionDialog 改为薄包装 同步更新 PlatformUiKit 收口文档与团队决策记录
This commit is contained in:
59
src/components/common/PlatformReportDialog.test.tsx
Normal file
59
src/components/common/PlatformReportDialog.test.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import { afterEach, expect, test, vi } from 'vitest';
|
||||
|
||||
import * as clipboardService from '../../services/clipboard';
|
||||
import { PlatformReportDialog } from './PlatformReportDialog';
|
||||
|
||||
vi.mock('../../services/clipboard', () => ({
|
||||
copyTextToClipboard: vi.fn(),
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
test('renders report fields and copies the joined report lines', async () => {
|
||||
vi.mocked(clipboardService.copyTextToClipboard).mockResolvedValue(true);
|
||||
|
||||
render(
|
||||
<PlatformReportDialog
|
||||
open
|
||||
title="统一报告"
|
||||
onClose={() => {}}
|
||||
copyIdleLabel="复制内容"
|
||||
fields={[
|
||||
{ label: '来源', value: '拼图草稿 puzzle-session-1' },
|
||||
{ label: '状态', value: '已完成', multiline: true },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
const dialog = screen.getByRole('dialog', { name: '统一报告' });
|
||||
expect(within(dialog).getByText('拼图草稿 puzzle-session-1')).toBeTruthy();
|
||||
expect(within(dialog).getByText('已完成')).toBeTruthy();
|
||||
|
||||
fireEvent.click(within(dialog).getByRole('button', { name: '复制内容' }));
|
||||
|
||||
expect(clipboardService.copyTextToClipboard).toHaveBeenCalledWith(
|
||||
['来源:拼图草稿 puzzle-session-1', '状态:已完成'].join('\n'),
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(within(dialog).getByRole('button', { name: '已复制' })).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
test('does not render report fields when closed', () => {
|
||||
render(
|
||||
<PlatformReportDialog
|
||||
open={false}
|
||||
title="统一报告"
|
||||
onClose={() => {}}
|
||||
copyIdleLabel="复制内容"
|
||||
fields={[]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByRole('dialog', { name: '统一报告' })).toBeNull();
|
||||
});
|
||||
87
src/components/common/PlatformReportDialog.tsx
Normal file
87
src/components/common/PlatformReportDialog.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { CopyFeedbackButton } from './CopyFeedbackButton';
|
||||
import { PlatformInfoBlock } from './PlatformInfoBlock';
|
||||
import { UnifiedModal } from './UnifiedModal';
|
||||
import { useCopyFeedback } from './useCopyFeedback';
|
||||
|
||||
export type PlatformReportDialogField = {
|
||||
label: string;
|
||||
value: string;
|
||||
multiline?: boolean;
|
||||
};
|
||||
|
||||
type PlatformReportDialogProps = {
|
||||
open: boolean;
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
copyIdleLabel: string;
|
||||
fields: readonly PlatformReportDialogField[];
|
||||
overlayClassName?: string;
|
||||
panelClassName?: string;
|
||||
};
|
||||
|
||||
function buildPlatformReportText(fields: readonly PlatformReportDialogField[]) {
|
||||
return fields.map((field) => `${field.label}:${field.value}`).join('\n');
|
||||
}
|
||||
|
||||
export function PlatformReportDialog({
|
||||
open,
|
||||
title,
|
||||
onClose,
|
||||
copyIdleLabel,
|
||||
fields,
|
||||
overlayClassName = 'platform-theme platform-theme--light !items-center',
|
||||
panelClassName = 'platform-remap-surface rounded-[1.5rem]',
|
||||
}: PlatformReportDialogProps) {
|
||||
const { copyState, copyText, resetCopyState } = useCopyFeedback();
|
||||
const reportText = useMemo(() => buildPlatformReportText(fields), [fields]);
|
||||
|
||||
useEffect(() => {
|
||||
resetCopyState();
|
||||
}, [open, reportText, resetCopyState]);
|
||||
|
||||
const copyReport = () => {
|
||||
if (!reportText) {
|
||||
return;
|
||||
}
|
||||
|
||||
void copyText(reportText);
|
||||
};
|
||||
|
||||
return (
|
||||
<UnifiedModal
|
||||
open={open}
|
||||
title={title}
|
||||
onClose={onClose}
|
||||
size="sm"
|
||||
overlayClassName={overlayClassName}
|
||||
panelClassName={panelClassName}
|
||||
bodyClassName="space-y-3 px-4 py-4 sm:px-5 sm:py-5"
|
||||
footerClassName="justify-end px-4 py-4 sm:px-5"
|
||||
footer={
|
||||
<CopyFeedbackButton
|
||||
state={copyState}
|
||||
onClick={copyReport}
|
||||
disabled={!reportText}
|
||||
idleLabel={copyIdleLabel}
|
||||
actionSurface="platform"
|
||||
actionFullWidth
|
||||
className="sm:w-auto"
|
||||
/>
|
||||
}
|
||||
>
|
||||
{open
|
||||
? fields.map((field) => (
|
||||
<PlatformInfoBlock
|
||||
key={`${field.label}-${field.value}`}
|
||||
label={field.label}
|
||||
multiline={field.multiline}
|
||||
>
|
||||
{field.value}
|
||||
</PlatformInfoBlock>
|
||||
))
|
||||
: null}
|
||||
</UnifiedModal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user