Skip to content

Commit cdab2cd

Browse files
committed
Keep the System header in view when Getting started opens it
Open System scrolled the Default model section with scrollIntoView, which also scrolled the page, whose overflow is hidden, and pushed its header out of sight. It now uses revealInPageBody, which scrolls only the page body and can focus another element than the one it reveals. The Getting started acceptance test checks the header stays in view for steps 2 and 4.
1 parent a637094 commit cdab2cd

4 files changed

Lines changed: 15 additions & 11 deletions

File tree

‎apps/web/DESIGN.md‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -678,9 +678,10 @@ do: Set up sandboxes, Add node, Open Nodes or Open sandbox backend; Open System;
678678
Create project (which continues to the new project's first key) or Issue key;
679679
See how to call (the newest active project, preferring one with an active key), or
680680
Projects and keys without an active project. Add node, Create project and Issue key
681-
open their page with the dialog already open; Open System focuses the default
682-
harness's Set or Replace; See how to call opens the project with its How to call
683-
heading at the top of the page body, focused. Every step done turns it into one line, "You're set", with Take the tour and
681+
open their page with the dialog already open; Open System brings the Default
682+
model section to the top of the page body and focuses the default harness's Set or
683+
Replace; See how to call opens the project with its How to call heading at the top
684+
of the page body, focused. Only the page body scrolls; the page header stays. Every step done turns it into one line, "You're set", with Take the tour and
684685
Dismiss; it stays, through the tour, until dismissed, and the checklist does not
685686
come back on its own. The choice is kept per installation in the browser, also
686687
while the deployment cannot be read; Show Getting started, a quiet row above the

‎apps/web/e2e/access.spec.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ test("opens a fresh install on the Overview's Getting started: a project and its
8383
await expect(page.getByRole("heading", { name: "My app", level: 1 })).toBeVisible();
8484
const call = page.getByRole("region", { name: "How to call" });
8585
await expect(call.getByRole("heading", { name: "How to call" })).toBeFocused();
86+
await expect(page.getByRole("heading", { name: "My app", level: 1 })).toBeInViewport();
8687
await expect(call.getByLabel("Shell", { exact: true })).toHaveText('export OPENAI_BASE_URL=https://core.example.com/v1\nexport OPENAI_API_KEY="<project API key>"');
8788
const stored = await browserStorage(page);
8889
expect(stored).not.toContain("fixture-secret");
@@ -98,6 +99,8 @@ test("leads from Getting started to the default model, and counts it done once t
9899
// It arrives on the default harness's action.
99100
const set = page.getByRole("button", { name: "Set the default model for Codex" });
100101
await expect(set).toBeFocused();
102+
// Only the page body scrolls to it: the page header stays in view.
103+
await expect(page.getByRole("heading", { name: "System", level: 1 })).toBeInViewport();
101104
await set.click();
102105
const form = page.getByRole("dialog", { name: "Set default model for Codex" });
103106
await form.getByLabel("Base URL").fill("https://model.example/v1");

‎apps/web/src/components/console-ui.tsx‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,16 @@ export function PageBody({ children, className }: { children: ReactNode; classNa
142142
}
143143

144144
/**
145-
* Scrolls the page body so `element` sits at its top, then focuses it. Only
146-
* the body scrolls: `scrollIntoView` would also shift the page, whose
147-
* overflow is hidden, and push the page header out of sight.
145+
* Scrolls the page body so `element` sits at its top, then focuses `focus`
146+
* (the element itself by default). Only the body scrolls: `scrollIntoView`
147+
* would also shift the page, whose overflow is hidden, and push the page
148+
* header out of sight.
148149
*/
149-
export function revealInPageBody(element: HTMLElement | null): void {
150+
export function revealInPageBody(element: HTMLElement | null, focus: HTMLElement | null = element): void {
150151
if (!element) return;
151152
const body = element.closest<HTMLElement>(".console-page-body");
152153
if (body) body.scrollTop += element.getBoundingClientRect().top - body.getBoundingClientRect().top - 12;
153-
element.focus({ preventScroll: true });
154+
focus?.focus({ preventScroll: true });
154155
}
155156

156157
export function Section({

‎apps/web/src/features/system/DefaultModelsSection.tsx‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useCallback, useId, useRef, useState, type ReactNode } from "react";
44
import { useTranslation } from "react-i18next";
55

66
import { ConfirmDialog } from "../../components/ConfirmDialog";
7-
import { EmptyState, HelpTip, Section, StatusDot } from "../../components/console-ui";
7+
import { EmptyState, HelpTip, revealInPageBody, Section, StatusDot } from "../../components/console-ui";
88
import { ErrorState } from "../../components/ErrorState";
99
import { Modal } from "../../components/Modal";
1010
import { TableSkeleton } from "../../components/Skeleton";
@@ -72,8 +72,7 @@ export function DefaultModelsSection() {
7272
window.requestAnimationFrame(() => {
7373
const section = document.getElementById("system-models-heading")?.closest("section");
7474
const action = section?.querySelector<HTMLButtonElement>("article[data-default] .system-model-actions button") ?? section?.querySelector<HTMLButtonElement>(".system-model-actions button");
75-
section?.scrollIntoView({ block: "start" });
76-
action?.focus();
75+
revealInPageBody(section ?? null, action ?? null);
7776
});
7877
});
7978

0 commit comments

Comments
 (0)