Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/e2e/scripts/run-all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ const ALWAYS = [
'src/tests/redis-editor.test.ts',
'src/tests/sql-editor-peek-row-form.test.ts',
'src/tests/sql-editor-peek-stacking.test.ts',
'src/tests/sql-editor-data-peek-grid.test.ts',
'src/tests/sql-editor-compare-multi-target.test.ts',
'src/tests/sql-editor-join-typing.test.ts',
].join(' '),
Expand Down
3 changes: 3 additions & 0 deletions apps/e2e/src/generated/test-ids.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,8 @@ export const TestIds = {
dataPeekBaseFilter: (id: string | number) => `data-peek-base-filter-${id}`,
/** button · Clear key filter and fetch all rows */
dataPeekBaseFilterClear: (id: string | number) => `data-peek-base-filter-clear-${id}`,
/** div */
dataPeekBody: (id: string | number) => `data-peek-body-${id}`,
/** button · Clear key filter and fetch all rows */
dataPeekClearKey: (id: string | number) => `data-peek-clear-key-${id}`,
/** button · Close data peek */
Expand Down Expand Up @@ -4039,6 +4041,7 @@ export type TestId =
| `data-peek-apply-${string}`
| `data-peek-base-filter-${string}`
| `data-peek-base-filter-clear-${string}`
| `data-peek-body-${string}`
| `data-peek-clear-key-${string}`
| 'data-peek-close'
| `data-peek-close-panel-${string}`
Expand Down
426 changes: 426 additions & 0 deletions apps/e2e/src/tests/sql-editor-data-peek-grid.test.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -1974,7 +1974,11 @@ export const DataGrid: React.FC<{
onChange={(e) => setSaveName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') commitSave();
if (e.key === 'Escape') setSavePrompt(null);
if (e.key === 'Escape') {
// Marked handled, so an enclosing Data Peek does not close too.
e.preventDefault();
setSavePrompt(null);
}
}}
placeholder="variable_name"
className="w-full bg-slate-950 border border-slate-700 rounded px-2 py-1.5 text-xs text-slate-100 outline-none accent-focus mb-2"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ describe('DataPeekPanel Insight tab', () => {
expect(peek?.entries[0]?.orderByClause).toBe('created_at DESC, id ASC');
});
});

it('gives its height to the grid, so filter rows never squeeze out the rows', () => {
// With the height on the whole panel, two WHERE rows and two ORDER BY rows
// took all of it: the grid showed its header and none of its rows.
render(<DataPeekPanel />);
fireEvent.click(screen.getByTestId('data-peek-where-add-e1'));
fireEvent.click(screen.getByTestId('data-peek-order-add-e1'));
expect(screen.getByTestId('data-peek-grid-e1').style.height).toBe('');
expect(screen.getByTestId('data-peek-body-e1').style.height).toBe('260px');
});
});

vi.mock('@/features/compare/state/useSyncStore', () => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,26 @@ import {
type PeekSort,
} from '@/shared/lib/tablePreview';
import type { InboundForeignKey } from '@/shared/lib/tablePreview';
import { clampDataPeekLimit } from '@/features/sql-editor/state/sqlEditorDataPeek';
import { DataGrid } from './DataGrid';
import { usePeekGridCrud } from './usePeekGridCrud';
import { PeekInsight } from './PeekInsight';
import { SQL_ICON_STROKE } from '@/shared/lib/iconStyle';
import type { TableSchema } from '@/shared/lib/types';
import { useSyncStore } from '@/features/compare';

const DEFAULT_HEIGHT_ROOT = 360;
/**
* The height of a panel's body — the rows grid or Insight — not of the whole
* panel. The controls above it (WHERE and ORDER BY rows, Limit) take what they
* need: with the height on the whole panel, each condition added took a row
* from the grid, and two filter rows left a grid of headers and no rows.
*/
const DEFAULT_BODY_HEIGHT_ROOT = 260;
/** FK drill panels stack full-width; main body scrolls when many are open. */
const DEFAULT_HEIGHT_DRILL = 320;
const MIN_PANEL_HEIGHT = 200;
const MAX_PANEL_HEIGHT = 900;
const DEFAULT_BODY_HEIGHT_DRILL = 220;
/** The store clamps to the same range (setDataPeekPanelHeight). */
const MIN_BODY_HEIGHT = 220;
const MAX_BODY_HEIGHT = 900;

/**
* Test id for one filter row. The first row keeps the original unsuffixed id,
Expand Down Expand Up @@ -84,7 +92,13 @@ const PeekFilterBar: React.FC<{
const draftOrderBy = orderByRef.current;
const draftLimit = limitRef.current;
const parsed = Number.parseInt(draftLimit, 10);
const nextLimit = Number.isFinite(parsed) ? parsed : entry.limit;
// Clamped here as the store does, and written back, so the box always shows
// the limit in force: an emptied box or a typo above the cap used to stay.
const nextLimit = Number.isFinite(parsed) ? clampDataPeekLimit(parsed) : entry.limit;
if (draftLimit !== String(nextLimit)) {
setLimit(String(nextLimit));
limitRef.current = String(nextLimit);
}
// Skip no-op applies (e.g. blur after an already-committed clear).
if (
draftWhere === entry.whereClause &&
Expand Down Expand Up @@ -344,8 +358,8 @@ const PeekResizeHandle: React.FC<{
target.setPointerCapture(e.pointerId);
const onMove = (ev: PointerEvent) => {
const next = Math.min(
MAX_PANEL_HEIGHT,
Math.max(MIN_PANEL_HEIGHT, startH.current + (ev.clientY - startY.current))
MAX_BODY_HEIGHT,
Math.max(MIN_BODY_HEIGHT, startH.current + (ev.clientY - startY.current))
);
setDataPeekPanelHeight(entryId, next);
};
Expand Down Expand Up @@ -497,8 +511,8 @@ const PeekGrid: React.FC<{
[links, entry, drillDataPeek]
);

const heightPx =
entry.panelHeightPx ?? (variant === 'drill' ? DEFAULT_HEIGHT_DRILL : DEFAULT_HEIGHT_ROOT);
const bodyHeightPx =
entry.panelHeightPx ?? (variant === 'drill' ? DEFAULT_BODY_HEIGHT_DRILL : DEFAULT_BODY_HEIGHT_ROOT);

const afterWrite = useCallback(() => runDataPeekEntry(entry.id), [runDataPeekEntry, entry.id]);
const columns = entry.result?.ok ? entry.result.columns : [];
Expand Down Expand Up @@ -543,7 +557,6 @@ const PeekGrid: React.FC<{
className={`px-2 pb-1 flex flex-col w-full shrink-0 transition-opacity ${
isDragging ? 'opacity-50' : ''
} ${isDragOver ? 'ring-2 ring-cyan-500/50 rounded-lg' : ''}`}
style={{ height: heightPx }}
data-testid={`data-peek-grid-${entry.id}`}
onDragOver={(e) => {
e.preventDefault();
Expand Down Expand Up @@ -644,11 +657,13 @@ const PeekGrid: React.FC<{
{crud.writeErrorBanner}

{tab === 'insight' && (
<PeekInsight
connectionId={connectionId}
tableName={entry.tableName}
schema={connectionSchema}
/>
<div className="min-h-0 flex flex-col" style={{ height: bodyHeightPx }} data-testid={`data-peek-body-${entry.id}`}>
<PeekInsight
connectionId={connectionId}
tableName={entry.tableName}
schema={connectionSchema}
/>
</div>
)}

{tab === 'rows' && entry.status === 'loading' && !entry.result && (
Expand All @@ -665,7 +680,7 @@ const PeekGrid: React.FC<{
)}

{tab === 'rows' && entry.result?.ok && (
<div className="flex-1 min-h-0 flex flex-col">
<div className="min-h-0 flex flex-col" style={{ height: bodyHeightPx }} data-testid={`data-peek-body-${entry.id}`}>
<DataGrid
result={entry.result}
exportName={entry.tableName}
Expand Down Expand Up @@ -727,7 +742,7 @@ const PeekGrid: React.FC<{
</div>
)}

<PeekResizeHandle entryId={entry.id} heightPx={heightPx} />
<PeekResizeHandle entryId={entry.id} heightPx={bodyHeightPx} />

{crud.overlays}
</div>
Expand All @@ -752,6 +767,10 @@ export const DataPeekPanel: React.FC = () => {
if (e.key !== 'Escape') return;
// Row editor / write confirm own Escape — do not close the whole peek.
if (childOverlayOpen) return;
// Something inside already used this Escape: the grid clearing a cell
// selection, or its save-as-variable dialog closing. Closing the peek
// too threw away every panel the reader had drilled open.
if (e.defaultPrevented) return;
closeDataPeek();
};
window.addEventListener('keydown', onKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export interface DataPeekEntry {
* Re-clicking the same FK replaces this panel; other FK columns stack.
*/
drillKey?: string;
/** Optional manual panel height (px); drag handle updates this. */
/** Optional manual height (px) of the panel's body, the grid or Insight; the drag handle sets it. */
panelHeightPx?: number;
/**
* Monotonic run id so a slow older execute can't overwrite a newer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2219,9 +2219,13 @@ export const useSqlEditorStore = create<SqlEditorState>()(
: { status: 'error', error: result.error, result }
);
} catch (error: unknown) {
// The rows of the last good page stay on screen, so the page number
// goes back to theirs. Left at the page that failed, the panel called
// page one's rows page two and numbered them from 11.
patchIfCurrent({
status: 'error',
error: error instanceof Error ? error.message : String(error),
...(entry.result?.ok ? { pageIndex: entry.resultPageIndex ?? 0 } : {}),
});
}
},
Expand Down
1 change: 1 addition & 0 deletions docs/testing/TEST_IDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,7 @@ Every control has a test ID: **997** buttons, text boxes, selects and textareas.
- `data-peek-apply-{id}` · button · Apply
- `data-peek-base-filter-{id}` · div
- `data-peek-base-filter-clear-{id}` · button · Clear key filter and fetch all rows
- `data-peek-body-{id}` · div
- `data-peek-clear-key-{id}` · button · Clear key filter and fetch all rows
- `data-peek-close` · button · Close data peek
- `data-peek-close-panel-{id}` · button · Close this panel
Expand Down
Loading