refactor: 更新PageErrorBoundary的文案和样式以对齐shadcn主题
- 错误提示从“该页面加载失败”改为“该功能运行异常” - 重试按钮文字从“重试”改为“重新尝试” - 使用border-destructive, bg-destructive等语义化类名替换硬编码颜色 - 更新对应测试断言
This commit is contained in:
@@ -14,7 +14,7 @@ interface State {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 页面级错误边界组件:捕获子组件树中的 JavaScript 错误
|
* 页面级错误边界组件:捕获子组件树中的 JavaScript 错误
|
||||||
* 与全局 ErrorBoundary 的区别:使用轻量内嵌卡片 UI,提供重试按钮
|
* 完美适配 shadcn/ui 语义化主题与暗黑模式
|
||||||
*/
|
*/
|
||||||
export class PageErrorBoundary extends Component<Props, State> {
|
export class PageErrorBoundary extends Component<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
@@ -43,27 +43,45 @@ export class PageErrorBoundary extends Component<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
if (this.state.hasError) {
|
if (this.state.hasError) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center flex-1 p-4 min-h-[200px]">
|
<div className="flex flex-col items-center justify-center flex-1 p-6 min-h-[300px] animate-in fade-in zoom-in-95 duration-200">
|
||||||
<div className="p-6 text-center rounded-xl border border-red-200 bg-red-50 max-w-md w-full">
|
{/*
|
||||||
<AlertCircle className="h-12 w-12 text-red-500 mx-auto mb-3" />
|
1. 适配暗黑模式的容器设计:
|
||||||
<h3 className="text-lg font-bold text-red-600 mb-2">该页面加载失败</h3>
|
不再使用 border-red-200 / bg-red-50,改用标准的 border-destructive/20 和 bg-destructive/5,
|
||||||
<p className="text-sm text-muted-foreground mb-4">
|
并在黑夜模式下会自动转为深红底色,绝不刺眼。
|
||||||
页面在加载或渲染时遇到错误,您可以重试或切换到其他工具。
|
*/}
|
||||||
|
<div className="p-6 text-center rounded-xl border border-destructive/20 bg-destructive/5 max-w-md w-full shadow-sm">
|
||||||
|
{/* 2. 状态符号改用标准的 text-destructive 语义色 */}
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-destructive/10 text-destructive mx-auto mb-4">
|
||||||
|
<AlertCircle className="h-6 w-6" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="text-base font-semibold text-foreground mb-1.5">该功能运行异常</h3>
|
||||||
|
<p className="text-xs text-muted-foreground mb-5">
|
||||||
|
该页面在加载或渲染时遇到了内部脚本错误。您可以尝试重试,或者通过导航菜单切换到其他工具。
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{/* 3. 错误日志展示:使用与 shadcn 贴合的深色代码块包裹 */}
|
||||||
{this.state.error && (
|
{this.state.error && (
|
||||||
<div className="mb-4 p-3 bg-muted rounded-lg text-left max-h-[160px] overflow-auto">
|
<div className="mb-5 p-3 rounded-lg bg-zinc-950 dark:bg-zinc-900 text-left max-h-40 overflow-y-auto border border-border/40">
|
||||||
<pre className="font-mono text-xs whitespace-pre-wrap break-all text-red-700">
|
<pre className="font-mono text-[11px] leading-relaxed whitespace-pre-wrap break-all text-zinc-200 selection:bg-zinc-700">
|
||||||
{this.state.error.toString()}
|
{this.state.error.stack || this.state.error.toString()}
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/*
|
||||||
|
4. 严谨调用 shadcn 原子 Button:
|
||||||
|
去掉全部手动指定的红底白字类名,直接启用 variant="destructive"。
|
||||||
|
它会自动处理 hover 颜色变化、暗黑模式切换以及无障碍高亮边框。
|
||||||
|
*/}
|
||||||
<Button
|
<Button
|
||||||
variant="default"
|
variant="destructive"
|
||||||
|
size="sm"
|
||||||
onClick={this.handleRetry}
|
onClick={this.handleRetry}
|
||||||
className="rounded-lg font-bold bg-red-600 hover:bg-red-700 text-white"
|
className="font-medium shadow-sm"
|
||||||
>
|
>
|
||||||
<RefreshCw className="mr-2 h-4 w-4" />
|
<RefreshCw className="mr-1.5 h-3.5 w-3.5" />
|
||||||
重试
|
重新尝试
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
||||||
expect(screen.getByText(/测试错误/)).toBeInTheDocument();
|
expect(screen.getByText(/测试错误/)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
||||||
|
|
||||||
// 将子组件替换为正常组件,然后点击重试
|
// 将子组件替换为正常组件,然后点击重试
|
||||||
rerender(
|
rerender(
|
||||||
@@ -54,14 +54,14 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const retryButton = screen.getByRole('button', { name: /重试/ });
|
const retryButton = screen.getByRole('button', { name: /重新尝试/ });
|
||||||
retryButton.click();
|
retryButton.click();
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(screen.getByTestId('normal-content')).toHaveTextContent('恢复后的内容');
|
expect(screen.getByTestId('normal-content')).toHaveTextContent('恢复后的内容');
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.queryByText('该页面加载失败')).not.toBeInTheDocument();
|
expect(screen.queryByText('该功能运行异常')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resetKey 变化时自动重置错误状态', async () => {
|
it('resetKey 变化时自动重置错误状态', async () => {
|
||||||
@@ -71,7 +71,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
||||||
|
|
||||||
// 切换 resetKey,同时提供正常子组件
|
// 切换 resetKey,同时提供正常子组件
|
||||||
rerender(
|
rerender(
|
||||||
@@ -84,7 +84,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
expect(screen.getByTestId('normal-content')).toHaveTextContent('页面 B 内容');
|
expect(screen.getByTestId('normal-content')).toHaveTextContent('页面 B 内容');
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.queryByText('该页面加载失败')).not.toBeInTheDocument();
|
expect(screen.queryByText('该功能运行异常')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('resetKey 不变时保持错误状态', () => {
|
it('resetKey 不变时保持错误状态', () => {
|
||||||
@@ -94,7 +94,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
||||||
|
|
||||||
// 仅 children 变化,resetKey 不变,错误应保持
|
// 仅 children 变化,resetKey 不变,错误应保持
|
||||||
rerender(
|
rerender(
|
||||||
@@ -103,7 +103,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(screen.getByText('该页面加载失败')).toBeInTheDocument();
|
expect(screen.getByText('该功能运行异常')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('错误 UI 包含重试按钮', () => {
|
it('错误 UI 包含重试按钮', () => {
|
||||||
@@ -113,7 +113,7 @@ describe('PageErrorBoundary', () => {
|
|||||||
</PageErrorBoundary>,
|
</PageErrorBoundary>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const retryButton = screen.getByRole('button', { name: /重试/ });
|
const retryButton = screen.getByRole('button', { name: /重新尝试/ });
|
||||||
expect(retryButton).toBeInTheDocument();
|
expect(retryButton).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user