mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-30 23:11:24 +08:00
🐛 fix(tool-center): 移除内嵌工具重复标题头
- 工具中心详情页统一承载入口标题和说明 - 数据同步、表结构比对和数据比对嵌入时隐藏内部 hero - 数据目录、快捷键、连接包和安全更新嵌入时隐藏内部 Modal 标题 - 补充工具中心全 pane 标题归属回归断言
This commit is contained in:
@@ -103,6 +103,60 @@ describe('tool center menu entries', () => {
|
||||
expect(appSource).toContain("borderBottom: `1px solid ${overlayTheme.divider}`");
|
||||
});
|
||||
|
||||
it('lets the tool center detail header own embedded tool titles', () => {
|
||||
const renderPaneStart = appSource.indexOf('const renderToolCenterPane = () => {');
|
||||
const renderPaneSource = appSource.slice(
|
||||
renderPaneStart,
|
||||
appSource.indexOf('};\n\n return (', renderPaneStart),
|
||||
);
|
||||
const connectionPackageSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'connection-package')"),
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'data-root')"),
|
||||
);
|
||||
const dataRootSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'data-root')"),
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'security-update')"),
|
||||
);
|
||||
const securityUpdateSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'security-update')"),
|
||||
renderPaneSource.indexOf("activeToolCenterPane.key === 'schema-compare'"),
|
||||
);
|
||||
const dataSyncSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("activeToolCenterPane.key === 'schema-compare'"),
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'drivers')"),
|
||||
);
|
||||
const driverSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'drivers')"),
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'snippet-settings')"),
|
||||
);
|
||||
const snippetSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'snippet-settings')"),
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'shortcut-settings')"),
|
||||
);
|
||||
const shortcutSource = renderPaneSource.slice(
|
||||
renderPaneSource.indexOf("if (activeToolCenterPane.key === 'shortcut-settings')"),
|
||||
renderPaneSource.indexOf('return null;', renderPaneSource.indexOf("if (activeToolCenterPane.key === 'shortcut-settings')")),
|
||||
);
|
||||
|
||||
expect(appSource).toContain('activeToolCenterPaneItem?.title ?? activeToolCenterGroup.title');
|
||||
expect(connectionPackageSource).toContain('<ConnectionPackagePasswordModal');
|
||||
expect(connectionPackageSource).not.toContain('renderUtilityModalTitle');
|
||||
expect(dataRootSource).toContain('title={null}');
|
||||
expect(dataRootSource).toContain('closable={false}');
|
||||
expect(dataRootSource).not.toContain('renderUtilityModalTitle');
|
||||
expect(securityUpdateSource).toContain('<SecurityUpdateSettingsModal');
|
||||
expect(securityUpdateSource).not.toContain('renderUtilityModalTitle');
|
||||
expect(dataSyncSource).toContain('<DataSyncModal');
|
||||
expect(dataSyncSource).not.toContain('renderUtilityModalTitle');
|
||||
expect(driverSource).toContain('<DriverManagerModal');
|
||||
expect(driverSource).not.toContain('renderUtilityModalTitle');
|
||||
expect(snippetSource).toContain('<SnippetSettingsModal');
|
||||
expect(snippetSource).not.toContain('renderUtilityModalTitle');
|
||||
expect(shortcutSource).toContain('title={null}');
|
||||
expect(shortcutSource).toContain('closable={false}');
|
||||
expect(shortcutSource).not.toContain('renderUtilityModalTitle');
|
||||
});
|
||||
|
||||
it('keeps the v2 AI entry in the sidebar and the legacy AI entry on the content edge', () => {
|
||||
expect(appSource).toContain('onToggleAI={toggleAIPanel}');
|
||||
expect(appSource).toContain('renderLegacyAIEdgeHandle');
|
||||
|
||||
@@ -3401,11 +3401,8 @@ function App() {
|
||||
<Modal
|
||||
embedded
|
||||
open
|
||||
title={renderUtilityModalTitle(
|
||||
<HddOutlined />,
|
||||
t('app.data_root.title'),
|
||||
t('app.data_root.description'),
|
||||
)}
|
||||
title={null}
|
||||
closable={false}
|
||||
onCancel={closeToolCenterPane}
|
||||
footer={[
|
||||
<Button key="close" onClick={closeToolCenterPane}>
|
||||
@@ -3551,11 +3548,8 @@ function App() {
|
||||
<Modal
|
||||
embedded
|
||||
open
|
||||
title={renderUtilityModalTitle(
|
||||
<LinkOutlined />,
|
||||
t('app.shortcuts.title'),
|
||||
t('app.shortcuts.description'),
|
||||
)}
|
||||
title={null}
|
||||
closable={false}
|
||||
onCancel={() => {
|
||||
setCapturingShortcutAction(null);
|
||||
closeToolCenterPane();
|
||||
|
||||
@@ -64,9 +64,10 @@ export default function ConnectionPackagePasswordModal({
|
||||
<Modal
|
||||
open={open}
|
||||
embedded={embedded}
|
||||
title={(
|
||||
title={embedded ? null : (
|
||||
<span style={{ minWidth: 0 }}>{title}</span>
|
||||
)}
|
||||
closable={embedded ? false : undefined}
|
||||
onCancel={onCancel}
|
||||
destroyOnHidden={false}
|
||||
maskClosable={false}
|
||||
|
||||
@@ -109,4 +109,10 @@ describe("DataSyncModal i18n", () => {
|
||||
/tr\(\s*(['"])data_sync\.compare_entry\.preview\.sql\.data_help\1/,
|
||||
);
|
||||
});
|
||||
|
||||
it('hides the modal hero when embedded in the tool center', () => {
|
||||
expect(source).toContain('{!embedded && (');
|
||||
expect(source).toContain('<div style={heroPanelStyle}>');
|
||||
expect(source).toMatch(/embedded\s*\?\s*\(\s*dataSyncContent\s*\)/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1388,71 +1388,73 @@ const DataSyncModal: React.FC<{
|
||||
const dataSyncContent = (
|
||||
<div style={modalWorkspaceStyle}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<div style={heroPanelStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: 12,
|
||||
alignItems: "flex-start",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 700,
|
||||
color: darkMode ? "#f8fafc" : "#0f172a",
|
||||
}}
|
||||
>
|
||||
{isMigrationWorkflow
|
||||
? tr("data_sync.title.migration")
|
||||
: isCompareEntry
|
||||
? entryPresentation.heroTitle
|
||||
: tr("data_sync.title.sync")}
|
||||
{!embedded && (
|
||||
<div style={heroPanelStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: 12,
|
||||
alignItems: "flex-start",
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 18,
|
||||
fontWeight: 700,
|
||||
color: darkMode ? "#f8fafc" : "#0f172a",
|
||||
}}
|
||||
>
|
||||
{isMigrationWorkflow
|
||||
? tr("data_sync.title.migration")
|
||||
: isCompareEntry
|
||||
? entryPresentation.heroTitle
|
||||
: tr("data_sync.title.sync")}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 6,
|
||||
fontSize: 13,
|
||||
lineHeight: 1.7,
|
||||
color: darkMode
|
||||
? "rgba(255,255,255,0.62)"
|
||||
: "rgba(15,23,42,0.62)",
|
||||
}}
|
||||
>
|
||||
{isMigrationWorkflow
|
||||
? tr("data_sync.title.migration_description")
|
||||
: isCompareEntry
|
||||
? entryPresentation.heroDescription
|
||||
: tr("data_sync.title.sync_description")}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 6,
|
||||
fontSize: 13,
|
||||
lineHeight: 1.7,
|
||||
color: darkMode
|
||||
? "rgba(255,255,255,0.62)"
|
||||
: "rgba(15,23,42,0.62)",
|
||||
}}
|
||||
>
|
||||
{isMigrationWorkflow
|
||||
? tr("data_sync.title.migration_description")
|
||||
: isCompareEntry
|
||||
? entryPresentation.heroDescription
|
||||
: tr("data_sync.title.sync_description")}
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
|
||||
<span style={badgeStyle}>
|
||||
{isMigrationWorkflow ? <RocketOutlined /> : <SwapOutlined />}{" "}
|
||||
{isMigrationWorkflow
|
||||
? tr("data_sync.badge.migration_mode")
|
||||
: isCompareEntry
|
||||
? entryPresentation.badgeText
|
||||
: tr("data_sync.badge.sync_mode")}
|
||||
</span>
|
||||
<span style={badgeStyle}>
|
||||
<DatabaseOutlined />{" "}
|
||||
{sourceConnId
|
||||
? tr("data_sync.badge.source_selected")
|
||||
: tr("data_sync.badge.source_pending")}
|
||||
</span>
|
||||
<span style={badgeStyle}>
|
||||
<TableOutlined />{" "}
|
||||
{tr("data_sync.badge.table_count", {
|
||||
count: selectedTables.length || 0,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
|
||||
<span style={badgeStyle}>
|
||||
{isMigrationWorkflow ? <RocketOutlined /> : <SwapOutlined />}{" "}
|
||||
{isMigrationWorkflow
|
||||
? tr("data_sync.badge.migration_mode")
|
||||
: isCompareEntry
|
||||
? entryPresentation.badgeText
|
||||
: tr("data_sync.badge.sync_mode")}
|
||||
</span>
|
||||
<span style={badgeStyle}>
|
||||
<DatabaseOutlined />{" "}
|
||||
{sourceConnId
|
||||
? tr("data_sync.badge.source_selected")
|
||||
: tr("data_sync.badge.source_pending")}
|
||||
</span>
|
||||
<span style={badgeStyle}>
|
||||
<TableOutlined />{" "}
|
||||
{tr("data_sync.badge.table_count", {
|
||||
count: selectedTables.length || 0,
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Steps current={currentStep} style={{ marginBottom: 24 }}>
|
||||
<Step title={tr("data_sync.step.configure")} />
|
||||
<Step title={tr("data_sync.step.select_tables")} />
|
||||
|
||||
@@ -134,6 +134,11 @@ describe('SecurityUpdateSettingsModal i18n source guards', () => {
|
||||
expect(source).not.toContain('当前项需要进一步处理后才能完成安全更新。');
|
||||
});
|
||||
|
||||
it('lets the tool center provide the title when embedded', () => {
|
||||
expect(source).toContain('title={embedded ? null : (');
|
||||
expect(source).toContain('closable={embedded ? false : undefined}');
|
||||
});
|
||||
|
||||
it('localizes settings chrome while preserving raw issue details, backup path and error text', async () => {
|
||||
const modalText = await renderSettingsModalText();
|
||||
expect(modalText).toContain('Security Update');
|
||||
|
||||
@@ -130,7 +130,7 @@ const SecurityUpdateSettingsModal = ({
|
||||
return (
|
||||
<Modal
|
||||
rootClassName={SECURITY_UPDATE_MODAL_CLASS}
|
||||
title={(
|
||||
title={embedded ? null : (
|
||||
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12, minWidth: 0 }}>
|
||||
<div
|
||||
style={{
|
||||
@@ -159,6 +159,7 @@ const SecurityUpdateSettingsModal = ({
|
||||
)}
|
||||
open={open}
|
||||
embedded={embedded}
|
||||
closable={embedded ? false : undefined}
|
||||
onCancel={onClose}
|
||||
footer={[
|
||||
showRetry ? (
|
||||
|
||||
@@ -931,6 +931,8 @@ describe("i18n catalog", () => {
|
||||
expect(passwordModalSource).not.toContain("密码已加密保护。如需通过公网传输,建议设置文件保护密码。");
|
||||
expect(passwordModalSource).not.toContain("导出连接密码");
|
||||
expect(passwordModalSource).not.toContain("设置文件保护密码");
|
||||
expect(passwordModalSource).toContain("title={embedded ? null : (");
|
||||
expect(passwordModalSource).toContain("closable={embedded ? false : undefined}");
|
||||
});
|
||||
|
||||
it("keeps QueryEditor format settings menu labels in catalogs instead of source literals", () => {
|
||||
|
||||
Reference in New Issue
Block a user