From 33c83b68df075a391b370da778de62d4e88a8760 Mon Sep 17 00:00:00 2001 From: danielvanza Date: Tue, 22 Sep 2026 23:11:27 +0200 Subject: [PATCH] Clamp sidebar resize to a minimum width --- .../core/StateProvider/graphViewLayout.test.ts | 12 ++++++++++++ .../src/core/StateProvider/graphViewLayout.ts | 6 +++++- .../StateProvider/graphViewLayoutDefaults.ts | 3 +++ .../SchemaGraph/Sidebar/schemaViewLayout.test.ts | 16 +++++++++++++++- .../SchemaGraph/Sidebar/schemaViewLayout.ts | 10 ++++++++-- 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/packages/graph-explorer/src/core/StateProvider/graphViewLayout.test.ts b/packages/graph-explorer/src/core/StateProvider/graphViewLayout.test.ts index fc08c33ce7..85b007aa95 100644 --- a/packages/graph-explorer/src/core/StateProvider/graphViewLayout.test.ts +++ b/packages/graph-explorer/src/core/StateProvider/graphViewLayout.test.ts @@ -13,6 +13,7 @@ import { import { DEFAULT_SIDEBAR_WIDTH, DEFAULT_TABLE_VIEW_HEIGHT, + MIN_SIDEBAR_WIDTH, } from "./graphViewLayoutDefaults"; const baseLayout: GraphViewLayout = { @@ -165,6 +166,17 @@ describe("useGraphViewSidebar", () => { expect(result.current.sidebarWidth).toBe(DEFAULT_SIDEBAR_WIDTH - 100); }); + it("should clamp sidebar width to the minimum on a large negative delta", () => { + const { result } = renderHookWithState( + () => useGraphViewSidebar(), + stateWithLayout(), + ); + + act(() => result.current.setSidebarWidth(-10000)); + + expect(result.current.sidebarWidth).toBe(MIN_SIDEBAR_WIDTH); + }); + it("should auto-open details when detailsAutoOpenOnSelection is true", () => { const { result } = renderHookWithState( () => useGraphViewSidebar(), diff --git a/packages/graph-explorer/src/core/StateProvider/graphViewLayout.ts b/packages/graph-explorer/src/core/StateProvider/graphViewLayout.ts index 836a874e76..86815ea070 100644 --- a/packages/graph-explorer/src/core/StateProvider/graphViewLayout.ts +++ b/packages/graph-explorer/src/core/StateProvider/graphViewLayout.ts @@ -4,6 +4,7 @@ import { useQueryEngine } from "../connector"; import { DEFAULT_SIDEBAR_WIDTH, DEFAULT_TABLE_VIEW_HEIGHT, + MIN_SIDEBAR_WIDTH, type GraphViewSidebarItem, type ToggleableView, } from "./graphViewLayoutDefaults"; @@ -96,7 +97,10 @@ export function useGraphViewSidebar() { ...prev, sidebar: { ...prev.sidebar, - width: (prev.sidebar?.width ?? DEFAULT_SIDEBAR_WIDTH) + deltaWidth, + width: Math.max( + MIN_SIDEBAR_WIDTH, + (prev.sidebar?.width ?? DEFAULT_SIDEBAR_WIDTH) + deltaWidth, + ), }, })); } diff --git a/packages/graph-explorer/src/core/StateProvider/graphViewLayoutDefaults.ts b/packages/graph-explorer/src/core/StateProvider/graphViewLayoutDefaults.ts index 2a1e096a4c..ea6370f123 100644 --- a/packages/graph-explorer/src/core/StateProvider/graphViewLayoutDefaults.ts +++ b/packages/graph-explorer/src/core/StateProvider/graphViewLayoutDefaults.ts @@ -50,6 +50,9 @@ export const DEFAULT_TABLE_VIEW_HEIGHT = 300; /** Default width for the graph view sidebar in pixels. */ export const DEFAULT_SIDEBAR_WIDTH = 400; +/** Floor for the sidebar width in pixels, so resizing cannot persist a non-positive width. */ +export const MIN_SIDEBAR_WIDTH = 200; + /** Initial layout state used when no persisted layout exists. */ export const defaultGraphViewLayout: GraphViewLayout = { activeToggles: new Set(["graph-viewer", "table-view"]), diff --git a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.test.ts b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.test.ts index 53bc11fa8a..66435aae16 100644 --- a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.test.ts +++ b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.test.ts @@ -3,7 +3,10 @@ import { act } from "react"; import type { SchemaViewLayout } from "@/core/StateProvider/schemaViewLayoutDefaults"; -import { DEFAULT_SIDEBAR_WIDTH } from "@/core/StateProvider/graphViewLayoutDefaults"; +import { + DEFAULT_SIDEBAR_WIDTH, + MIN_SIDEBAR_WIDTH, +} from "@/core/StateProvider/graphViewLayoutDefaults"; import { DbState, renderHookWithState } from "@/utils/testing"; import { useSchemaViewSidebar } from "./schemaViewLayout"; @@ -91,6 +94,17 @@ describe("useSchemaViewSidebar", () => { expect(result.current.sidebarWidth).toBe(DEFAULT_SIDEBAR_WIDTH - 50); }); + it("should clamp sidebar width to the minimum on a large negative delta", () => { + const { result } = renderHookWithState( + () => useSchemaViewSidebar(), + stateWithLayout(), + ); + + act(() => result.current.setSidebarWidth(-10000)); + + expect(result.current.sidebarWidth).toBe(MIN_SIDEBAR_WIDTH); + }); + it("should auto-open details tab when enabled and selection changes", () => { const { result } = renderHookWithState( () => useSchemaViewSidebar(), diff --git a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.ts b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.ts index 77d62a08c1..ee137623ee 100644 --- a/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.ts +++ b/packages/graph-explorer/src/modules/SchemaGraph/Sidebar/schemaViewLayout.ts @@ -2,7 +2,10 @@ import { useAtom } from "jotai"; import type { SchemaViewSidebarItem } from "@/core/StateProvider/schemaViewLayoutDefaults"; -import { DEFAULT_SIDEBAR_WIDTH } from "@/core/StateProvider/graphViewLayoutDefaults"; +import { + DEFAULT_SIDEBAR_WIDTH, + MIN_SIDEBAR_WIDTH, +} from "@/core/StateProvider/graphViewLayoutDefaults"; import { schemaViewLayoutAtom } from "@/core/StateProvider/storageAtoms"; export type { SchemaViewSidebarItem } from "@/core/StateProvider/schemaViewLayoutDefaults"; @@ -30,7 +33,10 @@ export function useSchemaViewSidebar() { ...prev, sidebar: { ...prev.sidebar, - width: (prev.sidebar?.width ?? DEFAULT_SIDEBAR_WIDTH) + deltaWidth, + width: Math.max( + MIN_SIDEBAR_WIDTH, + (prev.sidebar?.width ?? DEFAULT_SIDEBAR_WIDTH) + deltaWidth, + ), }, })); }