fix(connection): improve connection picker interactions

This commit is contained in:
zipg authored and GitHub committed 2026-07-23 22:48:38 +08:00
1 parent 3c04adbdd8
commit 9a5edae219
5 files changed
+134 -13

No files matched your search

@@ -0,0 +1,20 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
describe("connection dialog accessibility", () => {
it("moves initial focus to the dialog title instead of the dialog container", () => {
expect(dialogSource).toContain('<DialogTitle data-connection-dialog-title tabindex="-1">');
expect(dialogSource).toContain('querySelector<HTMLElement>("[data-connection-dialog-title]")?.focus({ preventScroll: true })');
expect(dialogSource).not.toMatch(/<DialogContent[^>]*\stabindex="-1"/);
});
it("reserves focus ring space around the scrollable category navigation", () => {
const nav = dialogSource.match(/<nav data-connection-category-nav class="([^"]+)"/)?.[1] ?? "";
expect(nav).toContain("px-0.5");
expect(nav).toContain("pt-0.5");
expect(nav).toContain("sm:py-0.5");
});
});
@@ -0,0 +1,11 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
describe("connection dialog corner style", () => {
it("uses the shared corner radius token for configuration controls", () => {
expect(dialogSource).toContain("border-radius: var(--dbx-radius-fixed-4, 4px);");
expect(dialogSource).not.toMatch(/\.connection-config-step[\s\S]*?border-radius:\s*4px;/);
});
});
@@ -0,0 +1,35 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { CONNECTION_PICKER_VIEW_STORAGE_KEY, loadConnectionPickerView, normalizeConnectionPickerView, saveConnectionPickerView } from "@/lib/connection/connectionPickerViewPreference";
const storage = new Map<string, string>();
describe("connection picker view preference", () => {
beforeEach(() => {
storage.clear();
vi.stubGlobal("localStorage", {
getItem: (key: string) => storage.get(key) ?? null,
setItem: (key: string, value: string) => storage.set(key, value),
removeItem: (key: string) => storage.delete(key),
});
});
afterEach(() => vi.unstubAllGlobals());
it("defaults missing and invalid values to icon view", () => {
expect(loadConnectionPickerView()).toBe("icon");
expect(normalizeConnectionPickerView("unknown")).toBe("icon");
});
it("persists and restores list view", () => {
saveConnectionPickerView("list");
expect(storage.get(CONNECTION_PICKER_VIEW_STORAGE_KEY)).toBe("list");
expect(loadConnectionPickerView()).toBe("list");
});
it("persists and restores icon view", () => {
saveConnectionPickerView("icon");
expect(loadConnectionPickerView()).toBe("icon");
});
});
@@ -0,0 +1,17 @@
import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStorage";
export type DbPickerView = "icon" | "list";
export const CONNECTION_PICKER_VIEW_STORAGE_KEY = "dbx-connection-picker-view";
export function normalizeConnectionPickerView(value: unknown): DbPickerView {
return value === "list" ? "list" : "icon";
}
export function loadConnectionPickerView(): DbPickerView {
return normalizeConnectionPickerView(safeLocalStorageGet(CONNECTION_PICKER_VIEW_STORAGE_KEY));
}
export function saveConnectionPickerView(view: DbPickerView) {
safeLocalStorageSet(CONNECTION_PICKER_VIEW_STORAGE_KEY, view);
}