Files
zeekayandHanzo Dev 61675c3f51 browser: 1.9.50 — the rail stops springing open
The collapsed rail widened whenever the pointer crossed it, and the collapse
toggle was hidden while collapsed, so hover was the ONLY way back: an
affordance you cannot see, on a control that moved the page out from under
whatever you were reaching for.

Collapse is an EXPLICIT, remembered choice now — the model console and
hanzo.app both settled on, whose pin/hover props they each marked deprecated
on the way. Collapsed, the mark IS the way back; open, it is the brand.

The labels stay in the markup and hidden in CSS, unchanged: that is what lets
the rail widen without re-rendering its contents. Only the trigger changed.

303 tests pass, including a case that pins the mark's two jobs.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 12:01:27 -07:00

69 lines
2.9 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { GuiProvider } from '@hanzo/gui';
import guiConfig from '@hanzo/ui/gui-config';
import { AnswerEngine } from '../src/answer/AnswerEngine.js';
import { Rail } from '../src/answer/Rail.js';
/**
* A new tab is a place to ask, so the surface opens with the rail collapsed and
* the composer holding the width.
*
* Collapsed is an ICON RAIL, not an absence: the brand and the way back stay on
* screen, and the labels are always in the markup and hidden in CSS, so widening
* the rail needs no re-render. Collapse is EXPLICIT — the rail never springs open
* under the pointer — so the mark itself has to be the way back.
*/
function markup(el: React.ReactElement): string {
return renderToStaticMarkup(
React.createElement(GuiProvider, { config: guiConfig, defaultTheme: 'dark' }, el),
);
}
const railProps = { toggle: () => {}, newThread: () => {}, signIn: () => {} };
describe('the answer rail', () => {
it('opens collapsed', () => {
const html = markup(
React.createElement(AnswerEngine, {
apiBase: 'https://api.hanzo.ai',
getToken: async () => null,
signIn: () => {},
}),
);
expect(html).toContain('ae-root collapsed');
});
it('keeps its affordances and its labels while collapsed', () => {
const shut = markup(React.createElement(Rail, { ...railProps, collapsed: true }));
// The way back is still clickable...
expect(shut).toContain('ae-brand');
expect(shut).toContain('ae-new');
// ...and the labels stay in the markup for CSS to reveal on expand,
// so widening the rail needs no re-render.
expect(shut).toContain('New thread');
});
it('makes the mark the way back when collapsed, and the brand when open', () => {
// While the rail opened on hover the mark had no job and the collapse
// toggle was hidden, so hover was the only way back — an affordance you
// cannot see. Collapse is explicit now, so the mark has to carry it.
const shut = markup(React.createElement(Rail, { ...railProps, collapsed: true }));
const open = markup(React.createElement(Rail, { ...railProps, collapsed: false }));
expect(shut).toContain('aria-label="Expand sidebar"');
expect(open).not.toContain('aria-label="Expand sidebar"');
expect(open).toContain('aria-label="Hanzo"');
});
it('announces which state the toggle will leave', () => {
const shut = markup(React.createElement(Rail, { ...railProps, collapsed: true }));
const open = markup(React.createElement(Rail, { ...railProps, collapsed: false }));
expect(shut).toContain('aria-expanded="false"');
expect(shut).toContain('aria-label="Expand sidebar"');
expect(open).toContain('aria-expanded="true"');
expect(open).toContain('aria-label="Collapse sidebar"');
});
});