refactor: 移除 Dashboard 页面及相关功能,更新路由和组件逻辑
- 移除 Dashboard 相关的组件、特性和测试代码,简化项目结构。 - 更新 PageSkeleton 组件,默认渲染工具页面骨架。 - 修改 RouterContainer 和 RouterProvider 以适应新的默认路由策略,确保用户直接访问 timestamp 页面。 - 更新测试用例,确保在新结构下的功能正常。
This commit is contained in:
@@ -4,30 +4,6 @@
|
||||
* 用于 Suspense fallback 和初始加载状态,提供平滑的视觉过渡
|
||||
* 避免白屏闪烁,减少布局偏移
|
||||
*/
|
||||
interface PageSkeletonProps {
|
||||
/** 骨架屏类型 */
|
||||
variant?: 'dashboard' | 'tool';
|
||||
}
|
||||
|
||||
/**
|
||||
* 仪表盘卡片骨架屏
|
||||
*/
|
||||
function DashboardCardSkeleton() {
|
||||
return (
|
||||
<div className="rounded-xl border border-border p-5 h-[100px]">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="flex gap-3 items-center">
|
||||
<div className="w-10 h-10 rounded-lg bg-muted animate-pulse" />
|
||||
<div>
|
||||
<div className="w-24 h-5 bg-muted rounded animate-pulse" />
|
||||
<div className="w-32 h-3.5 bg-muted rounded animate-pulse mt-1.5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-3 h-3 rounded-full bg-muted animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具页面骨架屏
|
||||
@@ -57,22 +33,9 @@ function ToolPageSkeleton() {
|
||||
|
||||
/**
|
||||
* 页面加载骨架屏
|
||||
*
|
||||
* @param props - PageSkeletonProps
|
||||
* @returns 骨架屏 JSX 元素
|
||||
*/
|
||||
export default function PageSkeleton({ variant = 'dashboard' }: PageSkeletonProps) {
|
||||
if (variant === 'tool') {
|
||||
return <ToolPageSkeleton />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-[repeat(auto-fill,minmax(300px,1fr))] auto-rows-fr gap-4 p-4">
|
||||
{Array.from({ length: 6 }).map((_, index) => (
|
||||
<DashboardCardSkeleton key={index} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
export default function PageSkeleton() {
|
||||
return <ToolPageSkeleton />;
|
||||
}
|
||||
|
||||
PageSkeleton.displayName = 'PageSkeleton';
|
||||
|
||||
@@ -30,7 +30,7 @@ function LoadedPage({ pageKey }: { pageKey: PageType }) {
|
||||
}, [pageKey]);
|
||||
|
||||
if (!Page) {
|
||||
return <PageSkeleton variant={pageKey === 'dashboard' ? 'dashboard' : 'tool'} />;
|
||||
return <PageSkeleton />;
|
||||
}
|
||||
|
||||
return <Page />;
|
||||
@@ -39,9 +39,6 @@ function LoadedPage({ pageKey }: { pageKey: PageType }) {
|
||||
export default function RouterContainer() {
|
||||
const { currentPage } = useRouter();
|
||||
|
||||
const animationClass =
|
||||
currentPage === 'dashboard' ? 'page-transition-dashboard' : 'page-transition-enter';
|
||||
|
||||
const currentFeature = getFeatureByKey(currentPage);
|
||||
|
||||
return (
|
||||
@@ -49,8 +46,7 @@ export default function RouterContainer() {
|
||||
key={currentPage}
|
||||
className={cn(
|
||||
'flex-1 flex flex-col overflow-x-hidden overflow-y-auto',
|
||||
'motion-reduce:transition-none',
|
||||
animationClass,
|
||||
'motion-reduce:transition-none page-transition-enter',
|
||||
)}
|
||||
>
|
||||
<PageErrorBoundary resetKey={currentPage}>
|
||||
|
||||
@@ -4,41 +4,17 @@ import PageSkeleton from '@/components/PageSkeleton';
|
||||
|
||||
describe('PageSkeleton 组件', () => {
|
||||
describe('渲染测试', () => {
|
||||
it('默认应渲染 dashboard 骨架屏', () => {
|
||||
it('应渲染工具页面骨架', () => {
|
||||
const { container } = render(<PageSkeleton />);
|
||||
|
||||
// dashboard 骨架屏包含 6 个卡片
|
||||
const cards = container.querySelectorAll('.rounded-xl');
|
||||
expect(cards.length).toBe(6);
|
||||
});
|
||||
|
||||
it('variant 为 dashboard 时应渲染仪表盘卡片骨架', () => {
|
||||
const { container } = render(<PageSkeleton variant="dashboard" />);
|
||||
|
||||
// 每个卡片有 2 个骨架元素(图标、文本),6 个卡片共 12 个
|
||||
const cards = container.querySelectorAll('.rounded-xl');
|
||||
expect(cards.length).toBe(6);
|
||||
});
|
||||
|
||||
it('variant 为 tool 时应渲染工具页面骨架', () => {
|
||||
const { container } = render(<PageSkeleton variant="tool" />);
|
||||
|
||||
// tool 骨架屏包含标题、输入区、控制栏 3 个按钮、结果区
|
||||
const skeletons = container.querySelectorAll('.animate-pulse');
|
||||
expect(skeletons.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('布局结构测试', () => {
|
||||
it('dashboard 骨架屏应使用 grid 布局', () => {
|
||||
const { container } = render(<PageSkeleton variant="dashboard" />);
|
||||
const gridContainer = container.firstChild as HTMLElement;
|
||||
|
||||
expect(gridContainer).toHaveClass('grid');
|
||||
});
|
||||
|
||||
it('tool 骨架屏应有内边距', () => {
|
||||
const { container } = render(<PageSkeleton variant="tool" />);
|
||||
it('工具骨架屏应有内边距', () => {
|
||||
const { container } = render(<PageSkeleton />);
|
||||
const toolContainer = container.firstChild as HTMLElement;
|
||||
|
||||
expect(toolContainer).toHaveClass('p-5');
|
||||
@@ -46,16 +22,8 @@ describe('PageSkeleton 组件', () => {
|
||||
});
|
||||
|
||||
describe('骨架屏元素测试', () => {
|
||||
it('dashboard 骨架屏应包含圆角和边框样式', () => {
|
||||
const { container } = render(<PageSkeleton variant="dashboard" />);
|
||||
|
||||
// 获取第一个卡片容器
|
||||
const card = container.querySelector('.rounded-xl.border');
|
||||
expect(card).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('tool 骨架屏应包含动画脉冲效果', () => {
|
||||
const { container } = render(<PageSkeleton variant="tool" />);
|
||||
it('工具骨架屏应包含动画脉冲效果', () => {
|
||||
const { container } = render(<PageSkeleton />);
|
||||
|
||||
const skeletons = container.querySelectorAll('.animate-pulse');
|
||||
expect(skeletons.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -6,12 +6,10 @@ import type { PageType } from '@/types/storage';
|
||||
import React from 'react';
|
||||
|
||||
const mockRouterValue = {
|
||||
currentPage: 'dashboard' as PageType,
|
||||
visiblePages: ['dashboard', 'timestamp'] as PageType[],
|
||||
currentPage: 'timestamp' as PageType,
|
||||
visiblePages: ['timestamp', 'jwt'] as PageType[],
|
||||
pageOrder: ['timestamp'] as PageType[],
|
||||
navigateTo: vi.fn(),
|
||||
syncNavigation: vi.fn(),
|
||||
goHome: vi.fn(),
|
||||
setVisiblePages: vi.fn(),
|
||||
setPageOrder: vi.fn(),
|
||||
recentlyUsedTools: [] as PageType[],
|
||||
@@ -34,21 +32,14 @@ describe('RouterContainer 组件', () => {
|
||||
|
||||
describe('渲染测试', () => {
|
||||
it('mount 后应直接渲染页面结构(不等待 storage 加载)', () => {
|
||||
mockRouterValue.currentPage = 'dashboard';
|
||||
mockRouterValue.currentPage = 'timestamp';
|
||||
const { container } = renderWithProvider(<RouterContainer />);
|
||||
expect(container.querySelector('.page-transition-dashboard')).toBeInTheDocument();
|
||||
expect(container.querySelector('.page-transition-enter')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('动画类测试', () => {
|
||||
it('在 dashboard 页面应应用 dashboard 动画类', () => {
|
||||
mockRouterValue.currentPage = 'dashboard';
|
||||
renderWithProvider(<RouterContainer />);
|
||||
const box = document.querySelector('.page-transition-dashboard');
|
||||
expect(box).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('在非 dashboard 页面应应用 enter 动画类', () => {
|
||||
it('应应用 enter 动画类', () => {
|
||||
mockRouterValue.currentPage = 'timestamp';
|
||||
renderWithProvider(<RouterContainer />);
|
||||
const box = document.querySelector('.page-transition-enter');
|
||||
@@ -60,7 +51,7 @@ describe('RouterContainer 组件', () => {
|
||||
it('currentPage 变化时应更新', () => {
|
||||
const { rerender } = renderWithProvider(<RouterContainer />);
|
||||
|
||||
mockRouterValue.currentPage = 'timestamp';
|
||||
mockRouterValue.currentPage = 'jwt';
|
||||
rerender(<RouterProvider>{<RouterContainer />}</RouterProvider>);
|
||||
|
||||
const box = document.querySelector('.page-transition-enter');
|
||||
@@ -70,11 +61,10 @@ describe('RouterContainer 组件', () => {
|
||||
|
||||
describe('页面级错误隔离', () => {
|
||||
it('PageErrorBoundary 应包裹页面内容', () => {
|
||||
mockRouterValue.currentPage = 'dashboard';
|
||||
mockRouterValue.currentPage = 'timestamp';
|
||||
const { container } = renderWithProvider(<RouterContainer />);
|
||||
|
||||
// 验证 RouterContainer 的 Box 结构存在
|
||||
const routerBox = container.querySelector('.page-transition-dashboard');
|
||||
const routerBox = container.querySelector('.page-transition-enter');
|
||||
expect(routerBox).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user