Skip to content

Commit 4dcbf06

Browse files
committed
fix(sql): keep SQL editor scrollable so the header stays visible
Constrain the SQL panel with flex + overflow so CodeMirror scrolls internally instead of growing the view. Fixes #1480.
1 parent b10686a commit 4dcbf06

2 files changed

Lines changed: 81 additions & 53 deletions

File tree

ui/studio/views/sql/SqlView.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,23 @@ describe("SqlView", () => {
12591259
harness.cleanup();
12601260
});
12611261

1262+
it("keeps the SQL editor in a bounded scroll region for long scripts", () => {
1263+
const { adapter } = createAdapterMock();
1264+
const studio = createStudioMock(adapter);
1265+
useStudioMock.mockReturnValue(studio);
1266+
1267+
const harness = renderSqlView();
1268+
1269+
const scrollRegion = harness.container.querySelector(
1270+
'[data-testid="sql-editor-scroll-container"]',
1271+
);
1272+
expect(scrollRegion).toBeTruthy();
1273+
expect(scrollRegion?.className).toContain("min-h-0");
1274+
expect(scrollRegion?.className).toContain("overflow-hidden");
1275+
1276+
harness.cleanup();
1277+
});
1278+
12621279
it("supports cancelling a running query", async () => {
12631280
const raw: Adapter["raw"] = async (_details, options) => {
12641281
return await new Promise((resolve) => {

ui/studio/views/sql/SqlView.tsx

Lines changed: 64 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { createSqlEditorSchemaFromIntrospection } from "../../../../data/sql-edi
2424
import { getTopLevelSqlStatementAtCursor } from "../../../../data/sql-statements";
2525
import { Button } from "../../../components/ui/button";
2626
import { Input } from "../../../components/ui/input";
27+
import { cn } from "../../../lib/utils";
2728
import { TableHead, TableRow } from "../../../components/ui/table";
2829
import { useColumnPinning } from "../../../hooks/use-column-pinning";
2930
import { useIntrospection } from "../../../hooks/use-introspection";
@@ -875,41 +876,47 @@ export function SqlView(_props: ViewProps) {
875876
) : null}
876877
</StudioHeader>
877878

878-
<div className="flex flex-col gap-3 p-3 border-b border-border bg-background">
879-
<div className="rounded-md border border-border overflow-hidden bg-background">
880-
<CodeMirror
881-
aria-label="SQL editor"
882-
basicSetup={{
883-
foldGutter: false,
884-
}}
885-
className={[
886-
"[&_.cm-editor]:!border-0 [&_.cm-editor]:font-mono",
887-
"[&_.cm-gutters]:border-r [&_.cm-gutters]:border-border [&_.cm-gutters]:bg-muted/30",
888-
"[&_.cm-line]:text-[15px] [&_.cm-scroller]:font-mono",
889-
].join(" ")}
890-
extensions={sqlEditorExtensions}
891-
minHeight="128px"
892-
onCreateEditor={(view) => {
893-
editorViewRef.current = view;
894-
const cursorIndex = view.state.doc.length;
895-
view.dispatch({
896-
selection: {
897-
anchor: cursorIndex,
898-
head: cursorIndex,
899-
},
900-
});
901-
view.focus();
902-
}}
903-
onChange={(value) => {
904-
hasUserEditedEditorValueRef.current = true;
905-
latestEditorValueRef.current = value;
906-
setEditorValue(value);
907-
}}
908-
placeholder="Write SQL..."
909-
theme={isDarkMode ? "dark" : "light"}
910-
value={editorValue}
911-
/>
912-
</div>
879+
<div className="flex min-h-0 flex-1 flex-col overflow-hidden">
880+
<div className="flex min-h-0 flex-1 flex-col gap-3 overflow-hidden border-b border-border bg-background p-3">
881+
<div
882+
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border border-border bg-background"
883+
data-testid="sql-editor-scroll-container"
884+
>
885+
<CodeMirror
886+
aria-label="SQL editor"
887+
basicSetup={{
888+
foldGutter: false,
889+
}}
890+
className={[
891+
"min-h-0 flex-1",
892+
"[&_.cm-editor]:!border-0 [&_.cm-editor]:font-mono",
893+
"[&_.cm-gutters]:border-r [&_.cm-gutters]:border-border [&_.cm-gutters]:bg-muted/30",
894+
"[&_.cm-line]:text-[15px] [&_.cm-scroller]:font-mono",
895+
].join(" ")}
896+
extensions={sqlEditorExtensions}
897+
height="100%"
898+
minHeight="128px"
899+
onCreateEditor={(view) => {
900+
editorViewRef.current = view;
901+
const cursorIndex = view.state.doc.length;
902+
view.dispatch({
903+
selection: {
904+
anchor: cursorIndex,
905+
head: cursorIndex,
906+
},
907+
});
908+
view.focus();
909+
}}
910+
onChange={(value) => {
911+
hasUserEditedEditorValueRef.current = true;
912+
latestEditorValueRef.current = value;
913+
setEditorValue(value);
914+
}}
915+
placeholder="Write SQL..."
916+
theme={isDarkMode ? "dark" : "light"}
917+
value={editorValue}
918+
/>
919+
</div>
913920
{aiGenerationErrorMessage ? (
914921
<div className="text-sm text-destructive">
915922
<strong>AI SQL generation error:</strong> {aiGenerationErrorMessage}
@@ -969,25 +976,29 @@ export function SqlView(_props: ViewProps) {
969976
) : null}
970977
</div>
971978
) : null}
972-
</div>
979+
</div>
973980

974-
<div
975-
data-testid="sql-result-grid-container"
976-
className="grow min-h-0 flex flex-col"
977-
>
978-
{result == null ? null : (
979-
<SqlResultGrid
980-
isRunning={isRunning}
981-
paginationState={paginationState}
982-
pinnedColumnIds={pinnedColumnIds}
983-
result={result}
984-
rowSelectionState={rowSelectionState}
985-
setPaginationState={setPaginationState}
986-
setPinnedColumnIds={setPinnedColumnIds}
987-
setRowSelectionState={setRowSelectionState}
988-
visualizationState={visualization.state}
989-
/>
990-
)}
981+
<div
982+
data-testid="sql-result-grid-container"
983+
className={cn(
984+
"flex min-h-0 flex-col",
985+
result != null ? "flex-1" : "flex-none",
986+
)}
987+
>
988+
{result == null ? null : (
989+
<SqlResultGrid
990+
isRunning={isRunning}
991+
paginationState={paginationState}
992+
pinnedColumnIds={pinnedColumnIds}
993+
result={result}
994+
rowSelectionState={rowSelectionState}
995+
setPaginationState={setPaginationState}
996+
setPinnedColumnIds={setPinnedColumnIds}
997+
setRowSelectionState={setRowSelectionState}
998+
visualizationState={visualization.state}
999+
/>
1000+
)}
1001+
</div>
9911002
</div>
9921003
</div>
9931004
);

0 commit comments

Comments
 (0)