From f7ec30f135e3f1017f776c130bb93863e44b6845 Mon Sep 17 00:00:00 2001 From: Elizabet Oliveira Date: Fri, 18 Sep 2026 11:44:59 +0200 Subject: [PATCH 1/3] fix(app): top-align toolbars next to growing query fields Keep date pickers, Run, and row actions level with the first line of a wrapping SQL or Lucene field. Sessions opts into PageHeader block padding so a taller search bar still has room. Co-authored-by: Cursor --- .claude/skills/page-layout/SKILL.md | 2 +- agent_docs/page_layout.md | 3 +- packages/app/src/DBSearchPage.tsx | 3 +- .../ServicesDashboardPage.tsx | 9 ++++- packages/app/src/SessionsPage.tsx | 4 +- .../DBEditTimeChartForm/ChartActionBar.tsx | 7 +++- .../ChartEditorControls.tsx | 13 ++++--- .../DBEditTimeChartForm/ChartSeriesEditor.tsx | 25 ++++++++---- packages/app/src/components/DBTracePanel.tsx | 3 +- .../app/src/components/KubernetesFilters.tsx | 9 ++++- .../app/src/components/PageHeader.module.scss | 10 ++++- packages/app/src/components/PageHeader.tsx | 37 +++++++++--------- .../SourceForm/HighlightedAttributes.tsx | 38 +++++++++++-------- .../Sources/SourceForm/MaterializedViews.tsx | 13 +++++-- .../Sources/SourceForm/OrderByFormRow.tsx | 30 ++++++++------- 15 files changed, 130 insertions(+), 76 deletions(-) diff --git a/.claude/skills/page-layout/SKILL.md b/.claude/skills/page-layout/SKILL.md index 7d2cadccd2..2a4577cc77 100644 --- a/.claude/skills/page-layout/SKILL.md +++ b/.claude/skills/page-layout/SKILL.md @@ -40,5 +40,5 @@ import { PageLayout } from '@/components/PageLayout'; | List page | `AlertsPage.tsx` | `PageHeader` + `title` + `Container` (no inputs in header) | | Tool page with inputs + hierarchy | `KubernetesDashboardPage.tsx`, `ClickhousePage.tsx` | `PageLayout` **without** `title`; **`breadcrumbs`** + `leading` + `actions` in one sticky header | | Tool page (top-level) | `DBServiceMapPage.tsx` | `PageLayout` **without** `title`; `leading` / `actions` only; no duplicate breadcrumb unless you add a real hierarchy | -| Custom toolbar | `SessionsPage.tsx` | `PageLayout` + `header` = custom `PageHeader` `children` (single-row inputs, no `title`) | +| Custom toolbar | `SessionsPage.tsx` | `PageLayout` + `header` = custom `PageHeader` `children` with `growing` (single-row inputs, no `title`) | | Custom title | `TeamPage.tsx` | `PageHeader` with `children` only | diff --git a/agent_docs/page_layout.md b/agent_docs/page_layout.md index 18d7274bb2..1c34590d26 100644 --- a/agent_docs/page_layout.md +++ b/agent_docs/page_layout.md @@ -35,8 +35,9 @@ Both live next to `withAppNav` in `packages/app/src/layout.tsx`, which wraps pag | `leading` | Left cluster: source picker, badges, or other controls. When inputs are present, **do not** pair with `title` for the same page name. | | `actions` | Right-aligned cluster: time range, Run/Save, sampling, refresh. | | `children` | Full custom header when slots are not enough. Do not combine with `title` / `leading` / `actions` / `breadcrumbs` unless you use the breadcrumbs-only branch. | +| `growing` | Opt in to block padding that only shows once the toolbar outgrows `min-height`. Sessions uses this for its multi-line search; title-only pages omit it. | -Header styling is defined in `PageHeader.module.scss`: sticky top, bottom border, horizontal padding `var(--mantine-spacing-sm)` (same as Search `px="sm"`). Single-row headers keep `min-height: 60px`; stacked header (breadcrumbs + toolbar) grows with content. +Header styling is defined in `PageHeader.module.scss`: sticky top, bottom border, horizontal padding `var(--mantine-spacing-sm)` (same as Search `px="sm"`). Single-row headers keep `min-height: 60px`; stacked header (breadcrumbs + toolbar) grows with content. Pass `growing` when the toolbar can outgrow that min-height (Sessions' multi-line search) so block padding appears only once the row is taller than the bar. ## PageLayout API diff --git a/packages/app/src/DBSearchPage.tsx b/packages/app/src/DBSearchPage.tsx index d93506e048..8b4d8ce23a 100644 --- a/packages/app/src/DBSearchPage.tsx +++ b/packages/app/src/DBSearchPage.tsx @@ -2394,7 +2394,8 @@ export function DBSearchPage() { - + + )} - + {filtersToggle != null && tabQueriesData(activeTab) && ( - + + + )} {(activeTab === 'table' || activeTab === 'pie' || diff --git a/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx b/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx index a172809faf..9087e5cf8e 100644 --- a/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx +++ b/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx @@ -34,6 +34,7 @@ import { isFormulaDisplayType, isFormulaSourceKind, } from '@/components/ChartEditor/utils'; +import { EDITOR_INPUT_HEIGHTS } from '@/components/editorInputHeights'; import MVOptimizationIndicator from '@/components/MaterializedViews/MVOptimizationIndicator'; import SearchWhereInput from '@/components/SearchInput/SearchWhereInput'; import SourceSchemaPreview, { @@ -325,13 +326,15 @@ export function ChartEditorControls({ <>
-
+ Group By -
+
{displayType === DisplayType.Table && ( <> -
+ Having -
+
{showWhere && ( <> - Where + + Where +
- - Group By - + + + Group By + +
{showHaving && ( <> - - Having - + + + Having + +
Trace ID Expression - + {/* The submit button stays level with the editor's first line as it grows */} + = ({ }; return ( - + {K8S_FILTER_FIELDS.map(({ field, placeholder, dataTestId }) => ( `, which lets it stay pinned for the full // length of the page rather than only the height of the header. const chromeStickyClass = hasStickyRow ? styles.notSticky : undefined; + const chromeClassName = classNames( + styles.header, + growing && styles.growing, + chromeStickyClass, + className, + ); const toolbarInner = ( <> @@ -118,21 +131,13 @@ export function PageHeader({ // it. A page with only a `stickyRow` (no breadcrumbs, no title) // skips the empty chrome entirely. children != null ? ( -
+
{children}
) : null ) : hasBreadcrumbs && hasToolbar ? (
{breadcrumbs}
@@ -140,22 +145,14 @@ export function PageHeader({
) : hasBreadcrumbs && !hasToolbar ? (
{breadcrumbs}
{children}
) : ( -
+
{toolbarInner}
); diff --git a/packages/app/src/components/Sources/SourceForm/HighlightedAttributes.tsx b/packages/app/src/components/Sources/SourceForm/HighlightedAttributes.tsx index 7b82510c19..2fe0c81631 100644 --- a/packages/app/src/components/Sources/SourceForm/HighlightedAttributes.tsx +++ b/packages/app/src/components/Sources/SourceForm/HighlightedAttributes.tsx @@ -8,6 +8,7 @@ import { IconTrash, } from '@tabler/icons-react'; +import { EDITOR_INPUT_HEIGHTS } from '@/components/editorInputHeights'; import { ErrorCollapse } from '@/components/Error/ErrorCollapse'; import { InputControlled } from '@/components/InputControlled'; import { SQLInlineEditorControlled } from '@/components/SQLEditor/SQLInlineEditor'; @@ -83,34 +84,39 @@ function HighlightedAttributeRow({
- - AS + {/* The label and icons stay level with the editors' first line as they grow */} + + + AS + - + + + + + + removeHighlightedAttribute(index)} > - + - - removeHighlightedAttribute(index)} - > - - + diff --git a/packages/app/src/components/Sources/SourceForm/MaterializedViews.tsx b/packages/app/src/components/Sources/SourceForm/MaterializedViews.tsx index 07ccdea0f3..764e6327b2 100644 --- a/packages/app/src/components/Sources/SourceForm/MaterializedViews.tsx +++ b/packages/app/src/components/Sources/SourceForm/MaterializedViews.tsx @@ -6,6 +6,7 @@ import { Box, Button, Divider, + Flex, Grid, Group, Select, @@ -19,6 +20,7 @@ import { IconCirclePlus, IconHelpCircle, IconTrash } from '@tabler/icons-react'; import { DatabaseSelectControlled } from '@/components/DatabaseSelect'; import { DBTableSelectControlled } from '@/components/DBTableSelect'; +import { EDITOR_INPUT_HEIGHTS } from '@/components/editorInputHeights'; import SelectControlled from '@/components/SelectControlled'; import { SQLInlineEditorControlled } from '@/components/SQLEditor/SQLInlineEditor'; import { useMetadataWithSettings } from '@/hooks/useMetadata'; @@ -556,7 +558,7 @@ function AggregatedColumnRow({ )} - + - - - + {/* Stays level with the editor's first line as it grows */} + + + + + diff --git a/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx b/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx index bcc09a0e73..8b3e87b6ae 100644 --- a/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx +++ b/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx @@ -6,6 +6,7 @@ import { ActionIcon, Box, Flex, Text, Tooltip } from '@mantine/core'; import { useDebouncedCallback, useDidUpdate } from '@mantine/hooks'; import { IconCheck } from '@tabler/icons-react'; +import { EDITOR_INPUT_HEIGHTS } from '@/components/editorInputHeights'; import { ErrorCollapse } from '@/components/Error/ErrorCollapse'; import { SQLInlineEditorControlled } from '@/components/SQLEditor/SQLInlineEditor'; import { useExplainQuery } from '@/hooks/useExplainQuery'; @@ -73,7 +74,7 @@ export function OrderByFormRow({ label="Default Order By" helpText="Custom ORDER BY expression that overrides the default ordering. Leave empty to use the auto-detected default. (This can be customized per search later)" > - + - - - - - + {/* Stays level with the editor's first line as it grows */} + + + + + + + {shouldShowResult && ( From c986a14d36387b635c602266856359046989a8a7 Mon Sep 17 00:00:00 2001 From: Elizabet Oliveira Date: Mon, 21 Sep 2026 11:26:37 +0100 Subject: [PATCH 2/3] fix(app): pin first-line wrappers to px and drop restated comments Mantine's `h` number is rem, so pass an explicit px height to stay aligned with the 36px editors. Growing headers skip the sticky-row padding reset. Adds a patch changeset. Co-authored-by: Cursor --- .changeset/top-align-growing-query-toolbars.md | 7 +++++++ packages/app/src/DBSearchPage.tsx | 1 - .../src/ServicesDashboardPage/ServicesDashboardPage.tsx | 1 - packages/app/src/SessionsPage.tsx | 1 - .../components/DBEditTimeChartForm/ChartActionBar.tsx | 2 +- .../DBEditTimeChartForm/ChartEditorControls.tsx | 5 ++--- .../components/DBEditTimeChartForm/ChartSeriesEditor.tsx | 9 ++++----- packages/app/src/components/DBTracePanel.tsx | 1 - packages/app/src/components/KubernetesFilters.tsx | 9 +-------- packages/app/src/components/PageHeader.module.scss | 2 +- .../Sources/SourceForm/HighlightedAttributes.tsx | 5 ++--- .../components/Sources/SourceForm/MaterializedViews.tsx | 3 +-- .../src/components/Sources/SourceForm/OrderByFormRow.tsx | 3 +-- 13 files changed, 20 insertions(+), 29 deletions(-) create mode 100644 .changeset/top-align-growing-query-toolbars.md diff --git a/.changeset/top-align-growing-query-toolbars.md b/.changeset/top-align-growing-query-toolbars.md new file mode 100644 index 0000000000..e7aa6981be --- /dev/null +++ b/.changeset/top-align-growing-query-toolbars.md @@ -0,0 +1,7 @@ +--- +'@hyperdx/app': patch +--- + +fix: keep toolbar controls level with the first line of a wrapping query field + +Date pickers, Run, and nearby row actions stay top-aligned as a SQL or Lucene field grows. Sessions opts into PageHeader block padding so a taller search bar still has room. diff --git a/packages/app/src/DBSearchPage.tsx b/packages/app/src/DBSearchPage.tsx index 8b4d8ce23a..087a2a1063 100644 --- a/packages/app/src/DBSearchPage.tsx +++ b/packages/app/src/DBSearchPage.tsx @@ -2394,7 +2394,6 @@ export function DBSearchPage() { diff --git a/packages/app/src/components/DBEditTimeChartForm/ChartActionBar.tsx b/packages/app/src/components/DBEditTimeChartForm/ChartActionBar.tsx index 07dd33ae3f..dbc9adf41e 100644 --- a/packages/app/src/components/DBEditTimeChartForm/ChartActionBar.tsx +++ b/packages/app/src/components/DBEditTimeChartForm/ChartActionBar.tsx @@ -157,7 +157,7 @@ export function ChartActionBar({ {filtersToggle != null && tabQueriesData(activeTab) && ( - + )} diff --git a/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx b/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx index 9087e5cf8e..f19100573d 100644 --- a/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx +++ b/packages/app/src/components/DBEditTimeChartForm/ChartEditorControls.tsx @@ -330,11 +330,10 @@ export function ChartEditorControls({ style={{ display: 'grid', gridTemplateColumns: 'auto minmax(0, 1fr)', - // Labels stay level with each editor's first line as it grows alignItems: 'start', }} > - + {displayType === DisplayType.Table && ( <> - + 0) { setValue('groupBy', stagedGroupBy.join(', ')); @@ -433,13 +433,12 @@ export function ChartSeriesEditor({ style={{ display: 'grid', gridTemplateColumns: 'auto 1fr auto 1fr', - // Labels stay level with each editor's first line as it grows alignItems: 'start', }} > {showWhere && ( <> - + Where
- + Group By @@ -491,7 +490,7 @@ export function ChartSeriesEditor({
{showHaving && ( <> - + Having diff --git a/packages/app/src/components/DBTracePanel.tsx b/packages/app/src/components/DBTracePanel.tsx index e23c2b7902..822eb208d4 100644 --- a/packages/app/src/components/DBTracePanel.tsx +++ b/packages/app/src/components/DBTracePanel.tsx @@ -397,7 +397,6 @@ export default function DBTracePanel({ {!traceId && parentSourceId != null && ( Trace ID Expression - {/* The submit button stays level with the editor's first line as it grows */} = ({ }; return ( - + {K8S_FILTER_FIELDS.map(({ field, placeholder, dataTestId }) => ( - {/* The label and icons stay level with the editors' first line as they grow */} - + AS - +
- {/* Stays level with the editor's first line as it grows */} - + diff --git a/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx b/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx index 8b3e87b6ae..4d11b4feb6 100644 --- a/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx +++ b/packages/app/src/components/Sources/SourceForm/OrderByFormRow.tsx @@ -88,8 +88,7 @@ export function OrderByFormRow({ disableKeywordAutocomplete /> - {/* Stays level with the editor's first line as it grows */} - + Date: Mon, 21 Sep 2026 11:55:17 +0100 Subject: [PATCH 3/3] fix(app): restore em dash byte in ChartSeriesEditor comment A previous commit rewrote the file through a non-UTF-8 locale, turning the em dash into a raw 0x97 byte. swc refused to read the file, breaking the app build, the Vercel deploy and every E2E shard. --- .../src/components/DBEditTimeChartForm/ChartSeriesEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/components/DBEditTimeChartForm/ChartSeriesEditor.tsx b/packages/app/src/components/DBEditTimeChartForm/ChartSeriesEditor.tsx index fec1bb8d81..4bd3a9b1bb 100644 --- a/packages/app/src/components/DBEditTimeChartForm/ChartSeriesEditor.tsx +++ b/packages/app/src/components/DBEditTimeChartForm/ChartSeriesEditor.tsx @@ -231,7 +231,7 @@ export function ChartSeriesEditor({ // Staged group-bys replace the chart's, same as the filters above: they // were chosen against this metric's tags. Only when something was staged - // though — group by is chart-level, so clearing it on every apply would + // though — group by is chart-level, so clearing it on every apply would // discard a grouping the user set by hand elsewhere. if (stagedGroupBy.length > 0) { setValue('groupBy', stagedGroupBy.join(', '));