diff --git a/apps/fabro-web/app/components/run-dock.test.tsx b/apps/fabro-web/app/components/run-dock.test.tsx index 56c5a1599..045b41445 100644 --- a/apps/fabro-web/app/components/run-dock.test.tsx +++ b/apps/fabro-web/app/components/run-dock.test.tsx @@ -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( + } + />, + ); + }); + 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; diff --git a/apps/fabro-web/app/components/run-dock.tsx b/apps/fabro-web/app/components/run-dock.tsx index 4e26daa3a..cb2a89c0e 100644 --- a/apps/fabro-web/app/components/run-dock.tsx +++ b/apps/fabro-web/app/components/run-dock.tsx @@ -99,7 +99,25 @@ export function RunDockShell({ className, )} > -
+ {/* 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. */} +
{ + if ((event.target as HTMLElement | null)?.closest?.("button")) { + return; + } + onCollapsedChange(false); + } + : undefined + } + > {/* A live region: the dock changing to a state that needs the operator has to reach assistive tech, not only the eye. */} diff --git a/apps/fabro-web/app/components/steer-bar.test.tsx b/apps/fabro-web/app/components/steer-bar.test.tsx index b13498af6..583a68750 100644 --- a/apps/fabro-web/app/components/steer-bar.test.tsx +++ b/apps/fabro-web/app/components/steer-bar.test.tsx @@ -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", diff --git a/apps/fabro-web/app/components/steer-bar.tsx b/apps/fabro-web/app/components/steer-bar.tsx index 1b6e61f7c..4ec85bbcb 100644 --- a/apps/fabro-web/app/components/steer-bar.tsx +++ b/apps/fabro-web/app/components/steer-bar.tsx @@ -57,7 +57,9 @@ export function SteerBar({ ref, }: SteerBarProps) { const [errorMessage, setErrorMessage] = useState(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(null); const steer = useSteerRun(runId); const interrupt = useInterruptRun(runId);