Merge pull request #699 from fabro-sh/steer-bar-default-closed

Collapse the steering bar by default and expand it on bar click
This commit is contained in:
Bryan Helmkamp 2026-08-01 09:29:50 -04:00 committed by GitHub
commit 61780d529e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 87 additions and 8 deletions

View file

@ -9,7 +9,7 @@ import {
import TestRenderer, { act } from "react-test-renderer";
import { setupReactTestEnv } from "../lib/test-utils";
import { DockComposer } from "./run-dock";
import { DockComposer, RunDockShell } from "./run-dock";
const mountedRenderers: TestRenderer.ReactTestRenderer[] = [];
let teardownReactEnv: (() => void) | undefined;
@ -26,6 +26,69 @@ afterEach(() => {
teardownReactEnv = undefined;
});
describe("RunDockShell", () => {
function mountShell(
collapsed: boolean,
onCollapsedChange: (collapsed: boolean) => void,
) {
let renderer!: TestRenderer.ReactTestRenderer;
act(() => {
renderer = TestRenderer.create(
<RunDockShell
label="Steer running agent"
tone="idle"
status="Steering"
collapsed={collapsed}
onCollapsedChange={onCollapsedChange}
actions={<div />}
/>,
);
});
mountedRenderers.push(renderer);
return renderer;
}
function clickableDivs(renderer: TestRenderer.ReactTestRenderer) {
return renderer.root.findAll(
(node) => node.type === "div" && node.props.onClick !== undefined,
);
}
test("the whole collapsed bar is a click target that expands it", () => {
const onCollapsedChange = mock((_collapsed: boolean) => undefined);
const renderer = mountShell(true, onCollapsedChange);
const [header] = clickableDivs(renderer);
expect(header).toBeDefined();
act(() => header.props.onClick({ target: { closest: () => null } }));
expect(onCollapsedChange).toHaveBeenCalledWith(false);
});
test("clicks on header buttons do not also expand the bar", () => {
const onCollapsedChange = mock((_collapsed: boolean) => undefined);
const renderer = mountShell(true, onCollapsedChange);
const [header] = clickableDivs(renderer);
act(() => header.props.onClick({ target: { closest: () => ({}) } }));
expect(onCollapsedChange).not.toHaveBeenCalled();
});
test("clicks with a non-element target still expand the bar", () => {
const onCollapsedChange = mock((_collapsed: boolean) => undefined);
const renderer = mountShell(true, onCollapsedChange);
const [header] = clickableDivs(renderer);
act(() => header.props.onClick({ target: {} }));
expect(onCollapsedChange).toHaveBeenCalledWith(false);
});
test("the expanded header is not a click target", () => {
const onCollapsedChange = mock((_collapsed: boolean) => undefined);
const renderer = mountShell(false, onCollapsedChange);
expect(clickableDivs(renderer)).toHaveLength(0);
});
});
describe("DockComposer", () => {
test("describes its keyboard behavior to assistive technology", () => {
let renderer!: TestRenderer.ReactTestRenderer;

View file

@ -99,7 +99,25 @@ export function RunDockShell({
className,
)}
>
<div className="flex shrink-0 items-center gap-2.5 px-5 py-2 sm:px-6">
{/* While collapsed, the whole bar expands on click. Clicks that land on
a button (Interrupt, the chevron) keep their own behavior. The
chevron button stays the keyboard/assistive-tech toggle. */}
<div
className={classNames(
"flex shrink-0 items-center gap-2.5 px-5 py-2 sm:px-6",
collapsed && "cursor-pointer",
)}
onClick={
collapsed
? (event) => {
if ((event.target as HTMLElement | null)?.closest?.("button")) {
return;
}
onCollapsedChange(false);
}
: undefined
}
>
<StatusDot tone={tone} />
{/* A live region: the dock changing to a state that needs the
operator has to reach assistive tech, not only the eye. */}

View file

@ -98,11 +98,7 @@ describe("SteerBar", () => {
});
mountedRenderers.push(renderer);
act(() => {
renderer.root
.findByProps({ "aria-label": "Collapse Steer running agent" })
.props.onClick();
});
// The dock starts collapsed by default.
expect(
renderer.root.findByProps({
"aria-label": "Expand Steer running agent",

View file

@ -57,7 +57,9 @@ export function SteerBar({
ref,
}: SteerBarProps) {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [collapsePreferred, setCollapsePreferred] = useState(false);
// Steering is an occasional control, so the dock starts collapsed and
// stays out of the way until the operator opens it.
const [collapsePreferred, setCollapsePreferred] = useState(true);
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
const steer = useSteerRun(runId);
const interrupt = useInterruptRun(runId);