From 8a56367d02399b56e793b988cf452cf046cb1704 Mon Sep 17 00:00:00 2001 From: Syngnat Date: Thu, 25 Jun 2026 18:42:11 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(query-editor):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=B7=A5=E5=85=B7=E6=A0=8F=E9=95=BF=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E6=8F=90=E7=A4=BA=E4=B8=8E=E7=89=87=E6=AE=B5=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SQL 编辑器连接和数据库选择器支持悬浮延迟显示完整名称 - SQL 片段管理嵌入工具中心时隐藏重复标题 - 缩小片段操作按钮并拆分滚动区域,避免底部内容被遮挡 - 补充工具栏和片段弹窗源码级回归断言 --- .../QueryEditor.external-sql-save.test.tsx | 12 ++- .../QueryEditor.results-and-drop.test.tsx | 12 ++- .../QueryEditorToolbar.layout.test.tsx | 25 +++++ .../src/components/QueryEditorToolbar.tsx | 51 +++++++++- .../SnippetSettingsModal.i18n.test.tsx | 99 ++++++++++++++++++- .../src/components/SnippetSettingsModal.tsx | 70 ++++++++----- frontend/src/styles/v2-theme-workbench.css | 8 ++ 7 files changed, 237 insertions(+), 40 deletions(-) diff --git a/frontend/src/components/QueryEditor.external-sql-save.test.tsx b/frontend/src/components/QueryEditor.external-sql-save.test.tsx index fb9e08ea..237e7dd2 100644 --- a/frontend/src/components/QueryEditor.external-sql-save.test.tsx +++ b/frontend/src/components/QueryEditor.external-sql-save.test.tsx @@ -7478,6 +7478,10 @@ describe('QueryEditor external SQL save', () => { expect(toolbarSource).toContain('gn-v2-query-toolbar-actions'); expect(toolbarSource).toContain('gn-v2-query-toolbar-connection-select'); expect(toolbarSource).toContain('gn-v2-query-toolbar-database-select'); + expect(toolbarSource).toContain('FULL_NAME_TOOLTIP_DELAY_SECONDS = 1'); + expect(toolbarSource).toContain('mouseEnterDelay={FULL_NAME_TOOLTIP_DELAY_SECONDS}'); + expect(toolbarSource).toContain('optionRender={(option) => renderFullNameSelectTooltip(option.data.fullName)}'); + expect(toolbarSource).toContain('labelRender={(option) => renderFullNameSelectTooltip(option.label ?? option.value)}'); expect(toolbarSource).toContain('gn-v2-query-toolbar-max-rows-select'); expect(toolbarSource).toContain('QueryEditorTransactionSettings'); expect(transactionSettingsSource).toContain('gn-v2-query-toolbar-transaction-mode-select'); @@ -7556,10 +7560,12 @@ describe('QueryEditor external SQL save', () => { expect(modalSource).toContain('data-sql-snippet-action-row="true"'); expect(modalSource).toContain('data-sql-snippet-content-region="true"'); expect(modalSource).toContain('data-sql-snippet-editor-scroll-region="true"'); - expect(modalSource).toContain("maxHeight: snippetModalBodyMaxHeight"); + expect(modalSource).toContain('maxHeight: embedded ? snippetModalEmbeddedBodyMaxHeight : snippetModalBodyMaxHeight'); + expect(modalSource).toContain('data-sql-snippet-syntax-reference-scroll-region="true"'); + expect(modalSource).toContain('data-sql-snippet-editor-panel-scroll-region="true"'); expect(modalSource).toContain("flex: '0 0 auto'"); - expect(modalSource).toContain("size=\"large\""); - expect(modalSource).toContain('minWidth: 96'); + expect(modalSource).toContain("size=\"middle\""); + expect(modalSource).toContain('minWidth: 84'); expect(modalSource).toContain('syntaxHelp'); expect(modalSource).toContain("t('snippet_settings.syntax_reference.label')"); expect(source).toContain('s.syntaxHelp || s.description || s.body'); diff --git a/frontend/src/components/QueryEditor.results-and-drop.test.tsx b/frontend/src/components/QueryEditor.results-and-drop.test.tsx index abe4293e..d755d04b 100644 --- a/frontend/src/components/QueryEditor.results-and-drop.test.tsx +++ b/frontend/src/components/QueryEditor.results-and-drop.test.tsx @@ -2712,6 +2712,10 @@ describe('QueryEditor external SQL save', () => { expect(toolbarSource).toContain('gn-v2-query-toolbar-actions'); expect(toolbarSource).toContain('gn-v2-query-toolbar-connection-select'); expect(toolbarSource).toContain('gn-v2-query-toolbar-database-select'); + expect(toolbarSource).toContain('FULL_NAME_TOOLTIP_DELAY_SECONDS = 1'); + expect(toolbarSource).toContain('mouseEnterDelay={FULL_NAME_TOOLTIP_DELAY_SECONDS}'); + expect(toolbarSource).toContain('optionRender={(option) => renderFullNameSelectTooltip(option.data.fullName)}'); + expect(toolbarSource).toContain('labelRender={(option) => renderFullNameSelectTooltip(option.label ?? option.value)}'); expect(toolbarSource).toContain('gn-v2-query-toolbar-max-rows-select'); expect(toolbarSource).toContain('QueryEditorTransactionSettings'); expect(transactionSettingsSource).toContain('gn-v2-query-toolbar-transaction-mode-select'); @@ -2789,10 +2793,12 @@ describe('QueryEditor external SQL save', () => { expect(modalSource).toContain('data-sql-snippet-action-row="true"'); expect(modalSource).toContain('data-sql-snippet-content-region="true"'); expect(modalSource).toContain('data-sql-snippet-editor-scroll-region="true"'); - expect(modalSource).toContain("maxHeight: snippetModalBodyMaxHeight"); + expect(modalSource).toContain('maxHeight: embedded ? snippetModalEmbeddedBodyMaxHeight : snippetModalBodyMaxHeight'); + expect(modalSource).toContain('data-sql-snippet-syntax-reference-scroll-region="true"'); + expect(modalSource).toContain('data-sql-snippet-editor-panel-scroll-region="true"'); expect(modalSource).toContain("flex: '0 0 auto'"); - expect(modalSource).toContain("size=\"large\""); - expect(modalSource).toContain('minWidth: 96'); + expect(modalSource).toContain("size=\"middle\""); + expect(modalSource).toContain('minWidth: 84'); expect(modalSource).toContain('syntaxHelp'); expect(modalSource).toContain("t('snippet_settings.syntax_reference.label')"); expect(source).toContain('s.syntaxHelp || s.description || s.body'); diff --git a/frontend/src/components/QueryEditorToolbar.layout.test.tsx b/frontend/src/components/QueryEditorToolbar.layout.test.tsx index 0f33e0d6..b3eedf0c 100644 --- a/frontend/src/components/QueryEditorToolbar.layout.test.tsx +++ b/frontend/src/components/QueryEditorToolbar.layout.test.tsx @@ -79,4 +79,29 @@ describe('QueryEditorToolbar layout', () => { expect(transactionDelayCss).toContain('width: 104px !important;'); expect(transactionDelayCss).toContain('flex: 0 0 104px !important;'); }); + + it('shows delayed full-name tooltips for truncated connection and database selectors', () => { + const toolbarSource = readFileSync(new URL('./QueryEditorToolbar.tsx', import.meta.url), 'utf8'); + const css = readV2ThemeCss(); + const connectionSelectSource = toolbarSource.slice( + toolbarSource.indexOf('gn-v2-query-toolbar-connection-select'), + toolbarSource.indexOf('gn-v2-query-toolbar-database-select'), + ); + const databaseSelectSource = toolbarSource.slice( + toolbarSource.indexOf('gn-v2-query-toolbar-database-select'), + toolbarSource.indexOf('gn-v2-query-toolbar-max-rows-select'), + ); + + expect(toolbarSource).toContain('FULL_NAME_TOOLTIP_DELAY_SECONDS = 1'); + expect(toolbarSource).toContain('mouseEnterDelay={FULL_NAME_TOOLTIP_DELAY_SECONDS}'); + expect(toolbarSource).toContain('renderFullNameSelectTooltip'); + expect(toolbarSource).toContain('gn-query-toolbar-select-full-name'); + expect(connectionSelectSource).toContain('optionRender={(option) => renderFullNameSelectTooltip(option.data.fullName)}'); + expect(connectionSelectSource).toContain('labelRender={(option) => renderFullNameSelectTooltip(option.label ?? option.value)}'); + expect(databaseSelectSource).toContain('optionRender={(option) => renderFullNameSelectTooltip(option.data.fullName)}'); + expect(databaseSelectSource).toContain('labelRender={(option) => renderFullNameSelectTooltip(option.label ?? option.value)}'); + expect(css).toContain('.gn-query-toolbar-select-full-name {'); + expect(css).toContain('text-overflow: ellipsis;'); + expect(css).toContain('white-space: nowrap;'); + }); }); diff --git a/frontend/src/components/QueryEditorToolbar.tsx b/frontend/src/components/QueryEditorToolbar.tsx index 35f7720c..be7130dc 100644 --- a/frontend/src/components/QueryEditorToolbar.tsx +++ b/frontend/src/components/QueryEditorToolbar.tsx @@ -55,6 +55,33 @@ type QueryEditorToolbarProps = { onAIAction: (action: "generate" | "explain" | "optimize" | "schema") => void; }; +const FULL_NAME_TOOLTIP_DELAY_SECONDS = 1; + +type FullNameSelectOption = { + label: string; + value: string; + fullName: string; +}; + +const renderFullNameSelectTooltip = (fullName: React.ReactNode) => { + const fullNameText = String(fullName ?? ""); + + return ( + + + {fullNameText} + + + ); +}; + const QueryEditorToolbar: React.FC = ({ isV2Ui, currentConnectionId, @@ -89,6 +116,17 @@ const QueryEditorToolbar: React.FC = ({ const i18n = useOptionalI18n(); const t = i18n?.t ?? defaultTranslate; const baseMoreMenuItems = saveMoreMenuItems ?? []; + const connectionSelectOptions: FullNameSelectOption[] = + queryCapableConnections.map((connection) => ({ + label: connection.name, + value: connection.id, + fullName: connection.name, + })); + const databaseSelectOptions: FullNameSelectOption[] = dbList.map((db) => ({ + label: db, + value: db, + fullName: db, + })); const toggleResultPanelShortcutLabel = toggleQueryResultsPanelShortcutBinding.enabled && toggleQueryResultsPanelShortcutBinding.combo @@ -172,10 +210,10 @@ const QueryEditorToolbar: React.FC = ({ placeholder={t("query_editor.placeholder.connection")} value={currentConnectionId} onChange={onConnectionChange} - options={queryCapableConnections.map((c) => ({ - label: c.name, - value: c.id, - }))} + options={connectionSelectOptions} + optionFilterProp="label" + optionRender={(option) => renderFullNameSelectTooltip(option.data.fullName)} + labelRender={(option) => renderFullNameSelectTooltip(option.label ?? option.value)} showSearch /> -
+
{t('snippet_settings.field.description.label')}
{t('snippet_settings.field.body.label')}
@@ -417,8 +436,9 @@ export default function SnippetSettingsModal({ onChange={(e) => setDraft((d) => ({ ...d, body: e.target.value }))} placeholder={'SELECT ${1:columns} FROM ${2:table_name}$0;'} style={{ - flex: 1, - minHeight: 120, + height: 220, + minHeight: 160, + maxHeight: 260, fontFamily: 'var(--gn-font-mono)', fontSize: 13, resize: 'none', @@ -428,7 +448,7 @@ export default function SnippetSettingsModal({ size="small" defaultActiveKey={['snippet-help']} items={syntaxHelpItems} - style={{ marginTop: 8, background: 'transparent' }} + style={{ marginTop: 8, background: 'transparent', flex: '0 0 auto' }} />
@@ -454,11 +474,11 @@ export default function SnippetSettingsModal({ style={{ display: 'flex', flex: '0 0 auto', - gap: 12, + gap: 10, justifyContent: 'flex-end', alignItems: 'center', - paddingTop: 12, - marginTop: 12, + paddingTop: 8, + marginTop: 8, borderTop: overlayTheme.sectionBorder, }} > @@ -468,7 +488,7 @@ export default function SnippetSettingsModal({ description={t('snippet_settings.confirm.reset.description')} onConfirm={() => handleReset(draft.id)} > - @@ -479,21 +499,21 @@ export default function SnippetSettingsModal({ description={t('snippet_settings.confirm.delete.description')} onConfirm={() => handleDelete(draft.id)} > - )} {showEditor && ( - )} - {onBack ? ( - ) : null} diff --git a/frontend/src/styles/v2-theme-workbench.css b/frontend/src/styles/v2-theme-workbench.css index 04873065..309655c8 100644 --- a/frontend/src/styles/v2-theme-workbench.css +++ b/frontend/src/styles/v2-theme-workbench.css @@ -148,6 +148,14 @@ body[data-ui-version="v2"] .gn-v2-query-toolbar-database-select { max-width: 220px; } +.gn-query-toolbar-select-full-name { + display: block; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + body[data-ui-version="v2"] .gn-v2-query-toolbar-max-rows-select { width: 132px !important; flex: 0 0 132px !important;