mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-08 11:31:57 +08:00
✨ feat(ui): 完成新版 UI 全量改造
- 整体布局:按新版 UI 重构左侧导航、对象树、连接分组和右键菜单体系 - 数据视图:优化 DDL 侧栏、横向滚动、筛选输入、编辑入口和虚拟表格体验 - AI 面板:重构新版入口、输入区、模型选择、快捷键和悬浮布局 - 标签与快捷键:补齐 Tab 悬浮信息、复制交互和 Mac/Windows 快捷键配置 - 工程质量:新增 v2 主题样式、菜单组件、外观工具和回归测试覆盖
This commit is contained in:
111
frontend/src/components/TabManager.hover.test.tsx
Normal file
111
frontend/src/components/TabManager.hover.test.tsx
Normal file
@@ -0,0 +1,111 @@
|
||||
import React from 'react';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { resolveTabHoverOpen, TabHoverInfo, stopTabHoverDragPropagation } from './TabManager';
|
||||
import type { TabData } from '../types';
|
||||
|
||||
describe('TabManager hover info', () => {
|
||||
it('memoizes the tab workbench so parent-only modal state does not repaint open tabs', () => {
|
||||
const source = readFileSync(new URL('./TabManager.tsx', import.meta.url), 'utf8');
|
||||
|
||||
expect(source).toContain('const TabManager: React.FC = React.memo(() => {');
|
||||
});
|
||||
|
||||
it('renders full v2 tab hover context for table tabs', () => {
|
||||
const tab: TabData = {
|
||||
id: 'conn-1-main-users',
|
||||
title: 'users',
|
||||
type: 'table',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
tableName: 'users',
|
||||
};
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
<TabHoverInfo
|
||||
tab={tab}
|
||||
displayTitle="[开发240] 表概览"
|
||||
connectionLabel="开发240"
|
||||
hostSummary="192.168.1.240"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(markup).toContain('data-tab-hover-info="true"');
|
||||
expect(markup).toContain('[开发240] 表概览');
|
||||
expect(markup).toContain('类型');
|
||||
expect(markup).toContain('表数据');
|
||||
expect(markup).toContain('连接');
|
||||
expect(markup).toContain('开发240');
|
||||
expect(markup).toContain('Host');
|
||||
expect(markup).toContain('192.168.1.240');
|
||||
expect(markup).toContain('数据库');
|
||||
expect(markup).toContain('main');
|
||||
expect(markup).toContain('对象');
|
||||
expect(markup).toContain('users');
|
||||
});
|
||||
|
||||
it('renders db identity for redis tabs without a database name', () => {
|
||||
const tab: TabData = {
|
||||
id: 'redis-keys-conn-1-db2',
|
||||
title: 'db2',
|
||||
type: 'redis-keys',
|
||||
connectionId: 'conn-1',
|
||||
redisDB: 2,
|
||||
};
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
<TabHoverInfo
|
||||
tab={tab}
|
||||
displayTitle="[缓存 | 10.0.0.8] db2"
|
||||
connectionLabel="缓存"
|
||||
hostSummary="10.0.0.8"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(markup).toContain('REDIS');
|
||||
expect(markup).toContain('Redis Key');
|
||||
expect(markup).toContain('未指定');
|
||||
expect(markup).toContain('db2');
|
||||
});
|
||||
|
||||
it('stops hover card pointer events from reaching tab drag listeners without blocking text selection', () => {
|
||||
const event = {
|
||||
preventDefault: vi.fn(),
|
||||
stopPropagation: vi.fn(),
|
||||
} as unknown as React.SyntheticEvent<HTMLElement>;
|
||||
|
||||
stopTabHoverDragPropagation(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
expect(event.stopPropagation).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('keeps tab hover hidden while the tab context menu is open', () => {
|
||||
expect(resolveTabHoverOpen(true, false)).toBe(true);
|
||||
expect(resolveTabHoverOpen(true, true)).toBe(false);
|
||||
expect(resolveTabHoverOpen(false, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('wires hover card tab-switch and drag-blocking handlers with selectable text styles', () => {
|
||||
const source = readFileSync(new URL('./TabManager.tsx', import.meta.url), 'utf8');
|
||||
|
||||
expect(source).toContain('onPointerDown={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onPointerUp={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onPointerDownCapture={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onMouseDown={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onMouseUp={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onClick={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onClickCapture={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onTouchStart={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('onTouchEnd={stopTabHoverDragPropagation}');
|
||||
expect(source).toContain('setIsHoverInfoOpen(false);');
|
||||
expect(source).toContain('setIsTabMenuOpen(true);');
|
||||
expect(source).toContain('open={resolveTabHoverOpen(isHoverInfoOpen, isTabMenuOpen)}');
|
||||
expect(source).toContain('onOpenChange={handleHoverInfoOpenChange}');
|
||||
expect(source).toContain('onOpenChange={handleTabMenuOpenChange}');
|
||||
expect(source).toMatch(/\.gn-v2-tab-hover-card \{[^}]*cursor: text;[^}]*user-select: text;/s);
|
||||
expect(source).toMatch(/\.gn-v2-tab-hover-card \* \{[^}]*user-select: text;/s);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user