mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 16:02:48 +08:00
🐛 fix(definition-viewer): 修复对象定义页滚动行号重叠
- 在 DefinitionViewer 只读 Monaco 实例上显式关闭 stickyScroll,避免滚动后出现悬浮代码行与行号叠层 - 同步关闭 TriggerViewer 的 stickyScroll,统一对象定义类只读查看器滚动表现 - 新增对象定义查看器 Monaco 选项回归测试,锁定只读定义页必须禁用 stickyScroll
This commit is contained in:
@@ -1019,6 +1019,7 @@ const DefinitionViewer: React.FC<DefinitionViewerProps> = ({ tab }) => {
|
||||
lineHeight: 24,
|
||||
lineNumbers: 'on',
|
||||
lineNumbersMinChars: 4,
|
||||
stickyScroll: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
automaticLayout: true,
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
import React from 'react';
|
||||
import { act, create } from 'react-test-renderer';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { TabData } from '../types';
|
||||
import { I18nProvider } from '../i18n/provider';
|
||||
import DefinitionViewer from './DefinitionViewer';
|
||||
import TriggerViewer from './TriggerViewer';
|
||||
|
||||
const storeState = vi.hoisted(() => ({
|
||||
connections: [
|
||||
{
|
||||
id: 'conn-1',
|
||||
name: 'local',
|
||||
config: {
|
||||
type: 'postgres',
|
||||
host: '127.0.0.1',
|
||||
port: 5432,
|
||||
user: 'postgres',
|
||||
password: '',
|
||||
database: 'main',
|
||||
},
|
||||
},
|
||||
],
|
||||
theme: 'light',
|
||||
addTab: vi.fn(),
|
||||
setActiveContext: vi.fn(),
|
||||
}));
|
||||
|
||||
const backendApp = vi.hoisted(() => ({
|
||||
DBQuery: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../store', () => ({
|
||||
useStore: (selector: (state: typeof storeState) => unknown) => selector(storeState),
|
||||
}));
|
||||
|
||||
vi.mock('../../wailsjs/go/app/App', () => backendApp);
|
||||
vi.mock('../i18n/runtime', () => ({
|
||||
syncLanguageRuntime: vi.fn(async () => undefined),
|
||||
}));
|
||||
|
||||
vi.mock('@ant-design/icons', () => ({
|
||||
EditOutlined: () => <span data-icon="edit" />,
|
||||
}));
|
||||
|
||||
vi.mock('./MonacoEditor', () => ({
|
||||
default: ({ value, options }: any) => (
|
||||
<pre
|
||||
data-editor="true"
|
||||
data-readonly={String(options?.readOnly)}
|
||||
data-sticky-scroll-enabled={String(options?.stickyScroll?.enabled)}
|
||||
>
|
||||
{value}
|
||||
</pre>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('antd', () => ({
|
||||
Spin: ({ tip }: any) => <div>{tip}</div>,
|
||||
Alert: ({ message, description }: any) => <div>{message}{description}</div>,
|
||||
Button: ({ children, onClick, icon }: any) => (
|
||||
<button type="button" onClick={onClick}>
|
||||
{icon}
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
const flushPromises = async (count = 6) => {
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
await Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
const renderWithI18n = (node: React.ReactElement) => (
|
||||
<I18nProvider
|
||||
preference="en-US"
|
||||
systemLanguages={['en-US']}
|
||||
onPreferenceChange={() => undefined}
|
||||
>
|
||||
{node}
|
||||
</I18nProvider>
|
||||
);
|
||||
|
||||
const routineTab: TabData = {
|
||||
id: 'routine-def-conn-1-main-reporting.refresh_stats',
|
||||
title: '函数: reporting.refresh_stats',
|
||||
type: 'routine-def',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
routineName: 'reporting.refresh_stats',
|
||||
routineType: 'FUNCTION',
|
||||
schemaName: 'reporting',
|
||||
};
|
||||
|
||||
const triggerTab: TabData = {
|
||||
id: 'trigger-conn-1-main-audit.users_bi',
|
||||
title: '触发器: audit.users_bi',
|
||||
type: 'trigger',
|
||||
connectionId: 'conn-1',
|
||||
dbName: 'main',
|
||||
triggerName: 'audit.users_bi',
|
||||
triggerTableName: 'audit.users',
|
||||
schemaName: 'audit',
|
||||
};
|
||||
|
||||
describe('Object definition viewers Monaco options', () => {
|
||||
beforeEach(() => {
|
||||
backendApp.DBQuery.mockReset();
|
||||
backendApp.DBQuery
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [{ routine_definition: 'CREATE OR REPLACE FUNCTION reporting.refresh_stats() RETURNS void AS $$ BEGIN END; $$ LANGUAGE plpgsql;' }],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
data: [{ trigger_definition: 'CREATE TRIGGER users_bi BEFORE INSERT ON audit.users EXECUTE FUNCTION audit.audit_users();' }],
|
||||
});
|
||||
});
|
||||
|
||||
it('disables sticky scroll for read-only routine definitions', async () => {
|
||||
let renderer: any;
|
||||
await act(async () => {
|
||||
renderer = create(renderWithI18n(<DefinitionViewer tab={routineTab} />));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
const editor = renderer.root.findByProps({ 'data-editor': 'true' });
|
||||
expect(editor.props['data-sticky-scroll-enabled']).toBe('false');
|
||||
});
|
||||
|
||||
it('disables sticky scroll for read-only trigger definitions', async () => {
|
||||
let renderer: any;
|
||||
await act(async () => {
|
||||
renderer = create(renderWithI18n(<TriggerViewer tab={triggerTab} />));
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
const editor = renderer.root.findByProps({ 'data-editor': 'true' });
|
||||
expect(editor.props['data-sticky-scroll-enabled']).toBe('false');
|
||||
});
|
||||
});
|
||||
@@ -589,6 +589,7 @@ LIMIT 1`];
|
||||
lineHeight: 24,
|
||||
lineNumbers: 'on',
|
||||
lineNumbersMinChars: 4,
|
||||
stickyScroll: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
wordWrap: 'on',
|
||||
automaticLayout: true,
|
||||
|
||||
Reference in New Issue
Block a user