🐛 fix(query-editor): 修复查找框全选被编辑器快捷键拦截

- 在可编辑目标内放行 Ctrl/Cmd+A 原生全选
- 保留文档级快捷键对 Monaco 编辑器的兜底全选
- 补充查找输入框与源码范围回归测试
This commit is contained in:
Syngnat
2026-07-22 17:45:51 +08:00
parent c067ad8a62
commit 0fb9857378
3 changed files with 75 additions and 2 deletions

View File

@@ -5036,6 +5036,74 @@ describe('QueryEditor external SQL save', () => {
expect(document.execCommand).not.toHaveBeenCalled();
});
it('leaves Ctrl/Cmd+A inside Monaco find inputs while retaining the editor fallback', async () => {
const windowListeners: Record<string, ((event?: any) => void)[]> = {};
vi.stubGlobal('window', {
addEventListener: vi.fn((type: string, listener: (event?: any) => void) => {
windowListeners[type] ||= [];
windowListeners[type].push(listener);
}),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
setTimeout,
clearTimeout,
requestAnimationFrame: vi.fn((callback: FrameRequestCallback) => {
callback(0);
return 1;
}),
cancelAnimationFrame: vi.fn(),
innerHeight: 900,
});
await act(async () => {
create(<QueryEditor tab={createTab({ query: 'SELECT * FROM users' })} />);
});
const OriginalHTMLElement = globalThis.HTMLElement;
class EditableInputTarget {
tagName = 'INPUT';
isContentEditable = false;
closest = vi.fn(() => null);
}
vi.stubGlobal('HTMLElement', EditableInputTarget as any);
editorState.editor.trigger.mockClear();
editorState.editor.focus.mockClear();
const findInputEvent = {
ctrlKey: true,
metaKey: false,
altKey: false,
shiftKey: false,
key: 'a',
target: new EditableInputTarget(),
preventDefault: vi.fn(),
stopPropagation: vi.fn(),
};
await act(async () => {
windowListeners.keydown?.forEach((listener) => listener(findInputEvent));
});
expect(findInputEvent.preventDefault).not.toHaveBeenCalled();
expect(findInputEvent.stopPropagation).not.toHaveBeenCalled();
expect(editorState.editor.trigger).not.toHaveBeenCalledWith('keyboard', 'editor.action.selectAll', null);
expect(editorState.editor.focus).not.toHaveBeenCalled();
const documentLevelEvent = {
...findInputEvent,
target: null,
preventDefault: vi.fn(),
stopPropagation: vi.fn(),
};
await act(async () => {
windowListeners.keydown?.forEach((listener) => listener(documentLevelEvent));
});
expect(documentLevelEvent.preventDefault).toHaveBeenCalled();
expect(documentLevelEvent.stopPropagation).toHaveBeenCalled();
expect(editorState.editor.trigger).toHaveBeenCalledWith('keyboard', 'editor.action.selectAll', null);
vi.stubGlobal('HTMLElement', OriginalHTMLElement);
});
it('intercepts Ctrl/Cmd+D at window level and duplicates the current line below', async () => {
storeState.shortcutOptions.duplicateCurrentLine.mac = { enabled: true, combo: 'Meta+D' };
storeState.shortcutOptions.duplicateCurrentLine.windows = { enabled: true, combo: 'Ctrl+D' };

View File

@@ -3289,8 +3289,13 @@ describe('QueryEditor external SQL save', () => {
it('keeps editor select-all scoped away from non-editor editable targets', () => {
const source = readFileSync(new URL('./QueryEditor.tsx', import.meta.url), 'utf8');
const selectAllSource = source.slice(
source.indexOf('const handleSelectAllInEditor = (event: KeyboardEvent) => {'),
source.indexOf("window.addEventListener('keydown', handleSelectAllInEditor, true);"),
);
expect(source).toContain("if (isEditableElement(event.target) && !inEditorPane) {");
expect(selectAllSource).toContain("if (isEditableElement(event.target)) {");
expect(selectAllSource).not.toContain("if (isEditableElement(event.target) && !inEditorPane) {");
});
it('keeps the embedded sql execution log limited to v2 query editor result tabs', () => {

View File

@@ -7533,7 +7533,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
const editorHasFocus = !!editor.hasTextFocus?.();
const inEditorPane = !!(targetNode && editorPaneRef.current?.contains(targetNode));
const inQueryEditor = !!(targetNode && queryEditorRootRef.current?.contains(targetNode));
if (isEditableElement(event.target) && !inEditorPane) {
if (isEditableElement(event.target)) {
return;
}
if (!editorHasFocus && !inEditorPane) {