fix(ui): keep driver store/settings page switching and tab active state (#7035)
This commit is contained in:
6 files changed
+50
-13
No files matched your search
+13
-10
@@ -535,6 +535,13 @@ function openSettings(initialTab = "appearance", initialSection?: string) {
|
|||||||
activateSettingsPage();
|
activateSettingsPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MainContentSurface = "query" | "settings" | "driverStore";
|
||||||
|
|
||||||
|
function activateMainContentSurface(surface: MainContentSurface) {
|
||||||
|
settingsStore.settingsPageActive = surface === "settings";
|
||||||
|
driverStoreActive.value = surface === "driverStore";
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => settingsStore.settingsNavigationRequest,
|
() => settingsStore.settingsNavigationRequest,
|
||||||
(request) => {
|
(request) => {
|
||||||
@@ -546,23 +553,20 @@ watch(
|
|||||||
|
|
||||||
function activateSettingsPage() {
|
function activateSettingsPage() {
|
||||||
settingsPageTabOpen.value = true;
|
settingsPageTabOpen.value = true;
|
||||||
settingsStore.settingsPageActive = true;
|
activateMainContentSurface("settings");
|
||||||
driverStoreActive.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function activateQuerySurface() {
|
function activateQuerySurface() {
|
||||||
driverStoreActive.value = false;
|
activateMainContentSurface("query");
|
||||||
settingsStore.settingsPageActive = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeSettingsPage() {
|
function closeSettingsPage() {
|
||||||
settingsPageTabOpen.value = false;
|
settingsPageTabOpen.value = false;
|
||||||
settingsStore.settingsPageActive = false;
|
|
||||||
if (settingsReturnSurface.value === "driverStore" && driverStoreTabOpen.value) {
|
if (settingsReturnSurface.value === "driverStore" && driverStoreTabOpen.value) {
|
||||||
driverStoreActive.value = true;
|
activateMainContentSurface("driverStore");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
driverStoreActive.value = false;
|
activateMainContentSurface("query");
|
||||||
}
|
}
|
||||||
|
|
||||||
const driverStoreFocus = ref<DriverStoreFocus | null>(null);
|
const driverStoreFocus = ref<DriverStoreFocus | null>(null);
|
||||||
@@ -578,13 +582,12 @@ function openDriverStorePage(target?: "agent" | "jdbc" | "storage" | "runtime" |
|
|||||||
driverStoreFocus.value = target ?? null;
|
driverStoreFocus.value = target ?? null;
|
||||||
}
|
}
|
||||||
driverStoreTabOpen.value = true;
|
driverStoreTabOpen.value = true;
|
||||||
driverStoreActive.value = true;
|
activateMainContentSurface("driverStore");
|
||||||
settingsStore.settingsPageActive = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeDriverStorePage() {
|
function closeDriverStorePage() {
|
||||||
driverStoreTabOpen.value = false;
|
driverStoreTabOpen.value = false;
|
||||||
driverStoreActive.value = false;
|
activateMainContentSurface("query");
|
||||||
driverStoreActiveTab.value = "agent";
|
driverStoreActiveTab.value = "agent";
|
||||||
driverStoreFocus.value = null;
|
driverStoreFocus.value = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2224,8 +2224,8 @@ watch(driverStoreTab, (tab) => {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AgentOfflineExportDialog v-model:open="offlineExportDialogOpen" :preview="offlineExportPreview" :loading="offlineExportLoading" :exporting="offlineExporting" :error="offlineExportError" @confirm="exportOfflinePackage" />
|
||||||
</div>
|
</div>
|
||||||
<AgentOfflineExportDialog v-model:open="offlineExportDialogOpen" :preview="offlineExportPreview" :loading="offlineExportLoading" :exporting="offlineExporting" :error="offlineExportError" @confirm="exportOfflinePackage" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
const source = readFileSync(new URL("../DriverStoreDialog.vue", import.meta.url), "utf8");
|
||||||
|
const templateStart = source.indexOf("<template>");
|
||||||
|
const template = source.slice(templateStart, source.indexOf("\n<style", templateStart)).trim();
|
||||||
|
|
||||||
|
describe("DriverStoreDialog visibility", () => {
|
||||||
|
it("keeps the page and its auxiliary dialog under one root so component v-show works", () => {
|
||||||
|
expect(template).toMatch(/^<template>\s*<div class="driver-store-view[^"]*">[\s\S]*<AgentOfflineExportDialog[^>]*\/>\s*<\/div>\s*<\/template>$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -500,6 +500,11 @@ function tabColorStyle(tab: QueryTab) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function specialTabActiveStyle(active: boolean | undefined): CSSProperties | undefined {
|
||||||
|
if (!active) return undefined;
|
||||||
|
return isClassicLayout.value ? { boxShadow: "inset 0 -2px 0 var(--ring)" } : { borderColor: "var(--ring)" };
|
||||||
|
}
|
||||||
|
|
||||||
function tabIconClass(tab: QueryTab) {
|
function tabIconClass(tab: QueryTab) {
|
||||||
if (tab.externalSqlFileMissing) return "text-amber-600 dark:text-amber-400";
|
if (tab.externalSqlFileMissing) return "text-amber-600 dark:text-amber-400";
|
||||||
if (tab.mode === "mq") return "";
|
if (tab.mode === "mq") return "";
|
||||||
@@ -764,7 +769,7 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
|
|||||||
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', settingsPageActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
|
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', settingsPageActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
|
||||||
: ['h-7 rounded-md border font-medium', settingsPageActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
|
: ['h-7 rounded-md border font-medium', settingsPageActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
|
||||||
"
|
"
|
||||||
:style="isClassicLayout && settingsPageActive ? { boxShadow: '0 1px 0 0 var(--color-background)' } : {}"
|
:style="specialTabActiveStyle(settingsPageActive)"
|
||||||
:data-active-tab="settingsPageActive"
|
:data-active-tab="settingsPageActive"
|
||||||
@click="emit('activate-settings-page')"
|
@click="emit('activate-settings-page')"
|
||||||
@mousedown.middle.prevent="emit('close-settings-page')"
|
@mousedown.middle.prevent="emit('close-settings-page')"
|
||||||
@@ -791,7 +796,7 @@ function onOverflowItemKeydown(event: KeyboardEvent, tabId: string, kind: "regul
|
|||||||
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', driverStoreActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
|
? ['h-full border-r border-border/80 dark:border-border/45 font-medium', driverStoreActive ? 'bg-background text-foreground' : 'text-foreground/70 hover:text-foreground/90']
|
||||||
: ['h-7 rounded-md border font-medium', driverStoreActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
|
: ['h-7 rounded-md border font-medium', driverStoreActive ? 'border-ring text-foreground' : 'border-border/60 text-foreground/70 hover:border-border hover:text-foreground/90']
|
||||||
"
|
"
|
||||||
:style="isClassicLayout && driverStoreActive ? { boxShadow: '0 1px 0 0 var(--color-background)' } : {}"
|
:style="specialTabActiveStyle(driverStoreActive)"
|
||||||
:data-active-tab="driverStoreActive"
|
:data-active-tab="driverStoreActive"
|
||||||
@click="emit('activate-driver-store')"
|
@click="emit('activate-driver-store')"
|
||||||
@mousedown.middle.prevent="emit('close-driver-store')"
|
@mousedown.middle.prevent="emit('close-driver-store')"
|
||||||
|
|||||||
@@ -95,6 +95,15 @@ describe("AppTabBar right-side close action", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("AppTabBar special page selection", () => {
|
||||||
|
it("shows the active settings or driver-manager tab with the same ring used by regular tabs", () => {
|
||||||
|
expect(tabBarSource).toContain("function specialTabActiveStyle(active: boolean | undefined)");
|
||||||
|
expect(tabBarSource).toContain('return isClassicLayout.value ? { boxShadow: "inset 0 -2px 0 var(--ring)" } : { borderColor: "var(--ring)" };');
|
||||||
|
expect(tabBarSource).toContain(':style="specialTabActiveStyle(settingsPageActive)"');
|
||||||
|
expect(tabBarSource).toContain(':style="specialTabActiveStyle(driverStoreActive)"');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("AppTabBar overflow search", () => {
|
describe("AppTabBar overflow search", () => {
|
||||||
it("filters every open tab by its display and source titles", () => {
|
it("filters every open tab by its display and source titles", () => {
|
||||||
expect(tabBarSource).toContain('const tabSearchQuery = ref("");');
|
expect(tabBarSource).toContain('const tabSearchQuery = ref("");');
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ const appSource = readFileSync(new URL("../../../App.vue", import.meta.url), "ut
|
|||||||
const settingsDialogSource = readFileSync(new URL("../../../components/editor/EditorSettingsDialog.vue", import.meta.url), "utf8");
|
const settingsDialogSource = readFileSync(new URL("../../../components/editor/EditorSettingsDialog.vue", import.meta.url), "utf8");
|
||||||
|
|
||||||
describe("settings page navigation", () => {
|
describe("settings page navigation", () => {
|
||||||
|
it("keeps query, settings, and driver-manager surfaces mutually exclusive", () => {
|
||||||
|
expect(appSource).toContain('type MainContentSurface = "query" | "settings" | "driverStore";');
|
||||||
|
expect(appSource).toMatch(/function activateMainContentSurface\(surface: MainContentSurface\) \{\s*settingsStore\.settingsPageActive = surface === "settings";\s*driverStoreActive\.value = surface === "driverStore";/);
|
||||||
|
expect(appSource).toMatch(/function activateSettingsPage\(\) \{\s*settingsPageTabOpen\.value = true;\s*activateMainContentSurface\("settings"\);/);
|
||||||
|
expect(appSource).toMatch(/function activateQuerySurface\(\) \{\s*activateMainContentSurface\("query"\);/);
|
||||||
|
expect(appSource).toMatch(/driverStoreTabOpen\.value = true;\s*activateMainContentSurface\("driverStore"\);/);
|
||||||
|
});
|
||||||
|
|
||||||
it("replays repeated navigation requests for the same tab", () => {
|
it("replays repeated navigation requests for the same tab", () => {
|
||||||
expect(appSource).toContain("settingsNavigationRequestId.value += 1");
|
expect(appSource).toContain("settingsNavigationRequestId.value += 1");
|
||||||
expect(appSource).toContain(':navigation-request-id="settingsNavigationRequestId"');
|
expect(appSource).toContain(':navigation-request-id="settingsNavigationRequestId"');
|
||||||
|
|||||||
Reference in new issue
Block a user