Fix sticky bottom anchoring on initial content growth

This commit is contained in:
Mohamed Boudra
2026-06-09 10:46:24 +07:00
parent 9ce6a38792
commit 6c3e2bd703
2 changed files with 55 additions and 5 deletions
@@ -560,6 +560,54 @@ describe("bottom anchor controller driver", () => {
expect(harness.driver.getSnapshot().mode).toBe("detached");
});
it("keeps initial native content growth anchored before layout scroll events arrive", () => {
const harness = createDriverHarness({
transportBehavior: {
verificationDelayFrames: 2,
verificationRetryMode: "recheck",
},
measurementState: createMeasurementState({
containerKey: "native-virtualized",
viewportWidth: 0,
viewportHeight: 0,
contentHeight: 0,
offsetY: 0,
viewportMeasuredForKey: null,
contentMeasuredForKey: null,
}),
});
harness.scrollToBottom.mockImplementation(() => {
harness.context.measurementState.offsetY = 0;
});
harness.context.measurementState.contentHeight = 1348;
harness.context.measurementState.contentMeasuredForKey = "native-virtualized";
harness.driver.handleContentSizeChange({
previousContentHeight: 0,
contentHeight: 1348,
});
expect(harness.scrollToBottom).toHaveBeenCalledTimes(1);
expect(harness.driver.getSnapshot()).toMatchObject({
mode: "sticky-bottom",
pendingVerification: {
requestId: null,
},
});
harness.context.measurementState.viewportWidth = 390;
harness.context.measurementState.viewportHeight = 546;
harness.context.measurementState.viewportMeasuredForKey = "native-virtualized";
harness.context.measurementState.offsetY = 50;
harness.context.nearBottom = false;
harness.driver.handleScrollNearBottomChange({
nextIsNearBottom: false,
scrollDelta: 50,
});
expect(harness.driver.getSnapshot().mode).toBe("sticky-bottom");
});
});
describe("controller helper predicates", () => {
@@ -560,6 +560,12 @@ function createBottomAnchorControllerDriver(
});
if (shouldRestick && !pendingRequest) {
pendingVerification = { requestId: null, retries: 0 };
if (attemptHandle) {
input.cancelFrame(attemptHandle);
attemptHandle = null;
}
runAttempt(false);
return;
}
if (shouldRestick || pendingRequest) {
evaluate(false, "content_size_change");
@@ -656,11 +662,7 @@ export const __private__ = {
previousContentHeight: number;
contentHeight: number;
}): boolean {
return (
input.mode === "sticky-bottom" &&
input.previousContentHeight > 0 &&
input.contentHeight > input.previousContentHeight
);
return input.mode === "sticky-bottom" && input.contentHeight > input.previousContentHeight;
},
shouldDetachFromScrollAway(input: {
mode: BottomAnchorMode;