fix(webui): position sidebar highlight on mount

This commit is contained in:
Xubin Ren
2026-08-01 23:01:43 +08:00
parent 0cb7dd5cc9
commit db6c9effc3
5 changed files with 77 additions and 56 deletions
+33 -11
View File
@@ -44,6 +44,7 @@ function rect({
describe("ChatList", () => {
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("orders chats by latest session activity by default", () => {
@@ -220,8 +221,20 @@ describe("ChatList", () => {
expect(within(chatsSection).queryByText("Project chat")).not.toBeInTheDocument();
});
it("positions one background highlight, then slides it between selected topics", () => {
it("positions one background highlight and resets it across hidden targets", () => {
let revealFrame: FrameRequestCallback | null = null;
let resizeObserverCallback: ResizeObserverCallback | null = null;
let activeTargetVisible = true;
class MockResizeObserver {
constructor(callback: ResizeObserverCallback) {
resizeObserverCallback = callback;
}
observe() {}
unobserve() {}
disconnect() {}
}
vi.stubGlobal("ResizeObserver", MockResizeObserver);
vi.spyOn(window, "requestAnimationFrame").mockImplementation((callback) => {
revealFrame = callback;
return 1;
@@ -232,7 +245,9 @@ describe("ChatList", () => {
return rect({ left: 0, top: 0, width: 300, height: 200 });
}
if (this.getAttribute("data-chat-row") === "websocket:active") {
return rect({ left: 8, top: 12, width: 284, height: 32 });
return activeTargetVisible
? rect({ left: 8, top: 12, width: 284, height: 32 })
: rect({ left: 0, top: 0, width: 0, height: 0 });
}
if (this.getAttribute("data-chat-row") === "websocket:inactive") {
return rect({ left: 8, top: 48, width: 284, height: 40 });
@@ -255,7 +270,7 @@ describe("ChatList", () => {
const { rerender } = render(
<ChatList
{...props}
activeKey={null}
activeKey="websocket:active"
/>,
);
@@ -265,16 +280,9 @@ describe("ChatList", () => {
"transition-[transform,width,height]",
"motion-reduce:transition-none",
);
expect(highlight).toHaveStyle("opacity: 0");
expect(screen.queryByTestId("sessions-selection-highlight-surface"))
.not.toBeInTheDocument();
rerender(
<ChatList
{...props}
activeKey="websocket:active"
/>,
);
expect(resizeObserverCallback).not.toBeNull();
const activeButton = screen.getByTitle("Active topic");
expect(activeButton).toHaveAttribute("aria-current", "page");
@@ -295,6 +303,17 @@ describe("ChatList", () => {
revealFrame?.(0);
expect(highlight.style.transitionProperty).toBe("");
activeTargetVisible = false;
resizeObserverCallback?.([], {} as ResizeObserver);
expect(highlight).toHaveStyle("opacity: 0");
activeTargetVisible = true;
resizeObserverCallback?.([], {} as ResizeObserver);
expect(highlight).toHaveStyle(
"width: 284px; height: 32px; transform: translate3d(8px, 12px, 0); opacity: 1; transition-property: none",
);
revealFrame?.(0);
rerender(
<ChatList
{...props}
@@ -307,6 +326,9 @@ describe("ChatList", () => {
expect(highlight).toHaveStyle(
"width: 284px; height: 40px; transform: translate3d(8px, 48px, 0)",
);
rerender(<ChatList {...props} activeKey={null} />);
expect(highlight).toHaveStyle("opacity: 0");
});
it("can collapse a project group and keeps project rename separate from chat titles", async () => {