Files
paseo/packages/app/src/hooks/use-container-width.test.ts
T
Mohamed Boudra 2298e9efdf Fix composer toolbar collapse flicker on tab switch (#2937)
Retained tabs stay mounted under display:none while inactive, which
reports a zero-width onLayout. ControlledAgentControls fed that
straight into its density resolver, so a hidden tab's toolbar
collapsed to its tightest density and painted one frame collapsed
before re-expanding when the tab became visible again.

Extract the zero-width guard that useContainerWidthBelow already had
into a shared readMeasuredWidth helper, and use it in the composer
toolbar's own layout handler so a display:none measurement can never
drive its density.

Also bump the chat outline rail's MIN_PANEL_WIDTH breakpoint (818 ->
918) so the rail disappears earlier under a narrower panel, and widen
the chat-outline e2e spec's viewport so its panel keeps healthy margin
over the new threshold.
2026-08-06 18:37:38 +08:00

91 lines
2.3 KiB
TypeScript

// @vitest-environment jsdom
import { act, renderHook } from "@testing-library/react";
import type { LayoutChangeEvent } from "react-native";
import { describe, expect, it } from "vitest";
import { readMeasuredWidth, useContainerWidthBelow } from "./use-container-width";
function layoutEvent(width: number): LayoutChangeEvent {
return {
nativeEvent: {
layout: {
width,
height: 48,
x: 0,
y: 0,
},
},
} as LayoutChangeEvent;
}
describe("readMeasuredWidth", () => {
it("returns the width of a rendered container", () => {
expect(readMeasuredWidth(layoutEvent(650))).toBe(650);
});
it("returns null for the zero width a hidden retained panel reports", () => {
expect(readMeasuredWidth(layoutEvent(0))).toBeNull();
});
});
describe("useContainerWidthBelow", () => {
it("does not re-render for width changes that stay in the same threshold bucket", () => {
let renderCount = 0;
const { result } = renderHook(() => {
renderCount += 1;
return useContainerWidthBelow(700);
});
expect(result.current.isBelow).toBe(true);
expect(renderCount).toBe(1);
act(() => {
result.current.onLayout(layoutEvent(650));
result.current.onLayout(layoutEvent(620));
result.current.onLayout(layoutEvent(699));
});
expect(result.current.isBelow).toBe(true);
expect(renderCount).toBe(1);
});
it("re-renders when the width crosses the threshold", () => {
let renderCount = 0;
const { result } = renderHook(() => {
renderCount += 1;
return useContainerWidthBelow(700);
});
act(() => {
result.current.onLayout(layoutEvent(760));
});
expect(result.current.isBelow).toBe(false);
expect(renderCount).toBe(2);
});
it("ignores zero-width measurements from hidden mounted content", () => {
let renderCount = 0;
const { result } = renderHook(() => {
renderCount += 1;
return useContainerWidthBelow(700, { initialIsBelow: false });
});
expect(result.current.isBelow).toBe(false);
act(() => {
result.current.onLayout(layoutEvent(0));
});
expect(result.current.isBelow).toBe(false);
expect(renderCount).toBe(1);
act(() => {
result.current.onLayout(layoutEvent(650));
});
expect(result.current.isBelow).toBe(true);
expect(renderCount).toBe(2);
});
});