Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 68 additions & 36 deletions src/app/features/room/RoomTimeline.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ const {
viewportSize: 600,
scrollToIndex: vi.fn<() => void>(),
scrollTo: vi.fn<() => void>(),
getItemOffset: () => 0,
getItemSize: () => 100,
getItemOffset: (): number => 0,
getItemSize: (): number => 100,
findItemIndex: () => 0,
},
timelineSync: {
Expand Down Expand Up @@ -380,6 +380,24 @@ const getScrollEl = (container: HTMLElement) => {
return scrollEl as Element;
};

const SCROLL_EXTENT = { scrollHeight: 1000, clientHeight: 600 };
const BOTTOM_OFFSET = SCROLL_EXTENT.scrollHeight - SCROLL_EXTENT.clientHeight;

const instrumentScrollEl = (container: HTMLElement) => {
const scrollEl = getScrollEl(container);
const scrollTo = vi.fn<(options: ScrollToOptions) => void>();
Object.defineProperty(scrollEl, 'scrollHeight', {
configurable: true,
get: () => SCROLL_EXTENT.scrollHeight,
});
Object.defineProperty(scrollEl, 'clientHeight', {
configurable: true,
get: () => SCROLL_EXTENT.clientHeight,
});
Object.assign(scrollEl, { scrollTo });
return scrollTo;
};

const renderTimeline = () => render(<RoomTimeline room={room} editor={{} as Editor} />);
const settleInitialScroll = () =>
act(async () => {
Expand Down Expand Up @@ -435,6 +453,8 @@ describe('RoomTimeline content ResizeObserver', () => {
vListHandle.viewportSize = 600;
vListHandle.scrollToIndex.mockReset();
vListHandle.scrollTo.mockReset();
vListHandle.getItemOffset = () => 0;
vListHandle.getItemSize = () => 100;
timelineSync.focusItem = undefined;
(timelineSync.setFocusItem as ReturnType<typeof vi.fn>).mockReset();
globalThis.ResizeObserver = ResizeObserverStub as unknown as typeof ResizeObserver;
Expand All @@ -446,86 +466,101 @@ describe('RoomTimeline content ResizeObserver', () => {

it('re-pins to the bottom when the VList content grows while pinned and live', async () => {
const { container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);

// Let the mount-time initial scroll and its 80ms timer settle, then
// isolate the content-resize behavior.
await settleInitialScroll();
vListHandle.scrollToIndex.mockClear();
scrollTo.mockClear();

const contentEl = getContentEl(container);
act(() => fireResize(contentEl));

expect(vListHandle.scrollToIndex).toHaveBeenCalledWith(
0,
expect.objectContaining({ align: 'end' })
);
expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
});

it('pins to the scroll extent instead of virtua item measurements', async () => {
const { container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);
vListHandle.getItemOffset = () => 0;
vListHandle.getItemSize = () => 0;

await settleInitialScroll();
scrollTo.mockClear();

act(() => fireResize(getContentEl(container)));

expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
expect(vListHandle.scrollToIndex).not.toHaveBeenCalled();
});

it('re-pins to the bottom when the timeline viewport shrinks while pinned and live', async () => {
const { container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);

await settleInitialScroll();
vListHandle.scrollToIndex.mockClear();
scrollTo.mockClear();

const timeline = container.querySelector('[data-testid="timeline"]');
expect(timeline).toBeTruthy();
act(() => fireResize(timeline!));

expect(vListHandle.scrollToIndex).toHaveBeenCalledWith(
0,
expect.objectContaining({ align: 'end' })
);
expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
});

it('does not re-pin on content growth after scrolling off the bottom', async () => {
const { container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);

await settleInitialScroll();

// Scroll far off the bottom: scrollSize - offset - viewportSize >= 100.
act(() => lastOnScroll?.(0));
vListHandle.scrollToIndex.mockClear();
scrollTo.mockClear();

const contentEl = getContentEl(container);
act(() => fireResize(contentEl));

expect(vListHandle.scrollToIndex).not.toHaveBeenCalled();
expect(scrollTo).not.toHaveBeenCalled();
});

it('cancels the delayed initial bottom scroll when the user scrolls up', async () => {
const { container } = renderTimeline();
vListHandle.scrollToIndex.mockClear();
const scrollTo = instrumentScrollEl(container);
scrollTo.mockClear();

act(() => {
getScrollEl(container).dispatchEvent(new Event('wheel', { bubbles: true }));
lastOnScroll?.(0);
});
await settleInitialScroll();

expect(vListHandle.scrollToIndex).not.toHaveBeenCalled();
expect(scrollTo).not.toHaveBeenCalled();
});

it('does not cancel the delayed initial bottom scroll for a Virtua scroll callback', async () => {
renderTimeline();
vListHandle.scrollToIndex.mockClear();
const { container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);
scrollTo.mockClear();

act(() => lastOnScroll?.(0));
await settleInitialScroll();

expect(vListHandle.scrollToIndex).toHaveBeenCalled();
expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
});

it('does not treat a pointer press as an initial timeline scroll', async () => {
const { container } = renderTimeline();
vListHandle.scrollToIndex.mockClear();
const scrollTo = instrumentScrollEl(container);
scrollTo.mockClear();

act(() => {
getScrollEl(container).dispatchEvent(new Event('pointerdown', { bubbles: true }));
lastOnScroll?.(0);
});
await settleInitialScroll();

expect(vListHandle.scrollToIndex).toHaveBeenCalled();
expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
});

it('resolves a jump target by event id, not by raw timeline index', async () => {
Expand All @@ -546,20 +581,18 @@ describe('RoomTimeline content ResizeObserver', () => {
});

it('treats a jump to the final live row as latest', async () => {
const { rerender, queryByText } = render(
const { rerender, queryByText, container } = render(
<RoomTimeline room={room} editor={{} as Editor} eventId="$evt1" />
);
const scrollTo = instrumentScrollEl(container);

await settleInitialScroll();
vListHandle.scrollToIndex.mockClear();
scrollTo.mockClear();

timelineSync.focusItem = { eventId: '$evt1', scrollTo: true, highlight: true };
rerender(<RoomTimeline room={room} editor={{} as Editor} eventId="$evt1" />);

expect(vListHandle.scrollToIndex).toHaveBeenCalledWith(
0,
expect.objectContaining({ align: 'end' })
);
expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
expect(queryByText('Jump to Latest')).toBeNull();
expect(navigateRoomMock).toHaveBeenCalledWith(room.roomId, undefined, { replace: true });
});
Expand Down Expand Up @@ -1159,36 +1192,35 @@ describe('scroll-edge pagination', () => {

describe('backfill scroll anchoring', () => {
it('re-pins to the bottom after a backfill completes if the user was at the bottom', async () => {
const { rerender } = renderTimeline();
const { rerender, container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);

// Let the mount-time initial scroll settle, then watch backfill only.
await settleInitialScroll();
vListHandle.scrollToIndex.mockClear();
scrollTo.mockClear();

timelineSync.backwardStatus = 'loading';
rerender(<RoomTimeline room={room} editor={{} as Editor} />);
timelineSync.backwardStatus = 'idle';
rerender(<RoomTimeline room={room} editor={{} as Editor} />);

expect(vListHandle.scrollToIndex).toHaveBeenCalledWith(
0,
expect.objectContaining({ align: 'end' })
);
expect(scrollTo).toHaveBeenCalledWith({ top: BOTTOM_OFFSET, behavior: 'instant' });
});

it('does not scroll away after a backfill if the user had scrolled up', async () => {
const { rerender } = renderTimeline();
const { rerender, container } = renderTimeline();
const scrollTo = instrumentScrollEl(container);

await settleInitialScroll();

act(() => lastOnScroll?.(0));
vListHandle.scrollToIndex.mockClear();
scrollTo.mockClear();

timelineSync.backwardStatus = 'loading';
rerender(<RoomTimeline room={room} editor={{} as Editor} />);
timelineSync.backwardStatus = 'idle';
rerender(<RoomTimeline room={room} editor={{} as Editor} />);

expect(vListHandle.scrollToIndex).not.toHaveBeenCalled();
expect(scrollTo).not.toHaveBeenCalled();
});
});
15 changes: 6 additions & 9 deletions src/app/features/room/RoomTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useMessageEdit } from '$hooks/useMessageEdit';
import { useDocumentFocusChange } from '$hooks/useDocumentFocusChange';
import { useIsInactivePanel } from '$hooks/useRoom';
import { markAsRead } from '$utils/notifications';
import { isWindowFocused } from '$utils/dom';
import { isWindowFocused, scrollToBottom as scrollElementToBottom } from '$utils/dom';
import { today, yesterday, timeDayMonthYear } from '$utils/time';
import {
unwrapRelationJumpTarget,
Expand Down Expand Up @@ -492,18 +492,15 @@ export function RoomTimeline({
const lastIndex = processedEventsRef.current.length - 1;
if (!v || lastIndex < 0) return;

const smooth = behavior === 'smooth' && !reducedMotion;

const scrollEl = scrollElRef.current;
let offset = 0;
if (scrollEl) {
const target = v.getItemOffset(lastIndex) + v.getItemSize(lastIndex) - v.viewportSize;
offset = Math.max(0, scrollEl.scrollHeight - scrollEl.clientHeight - target);
scrollElementToBottom(scrollEl, smooth ? 'smooth' : 'instant');
return;
}

v.scrollToIndex(lastIndex, {
align: 'end',
offset,
smooth: behavior === 'smooth' && !reducedMotion,
});
v.scrollToIndex(lastIndex, { align: 'end', smooth });
},
[reducedMotion]
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@
new Promise((resolve) => {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > scrolls to the nearest visible row when the jump target is filtered out

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > cancels a pending context load when opening an already-rendered event

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > remounts the virtualizer when switching to a focused timeline window

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > does not treat a pointer press as an initial timeline scroll

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > does not cancel the delayed initial bottom scroll for a Virtua scroll callback

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > cancels the delayed initial bottom scroll when the user scrolls up

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > does not re-pin on content growth after scrolling off the bottom

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > re-pins to the bottom when the timeline viewport shrinks while pinned and live

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > pins to the scroll extent instead of virtua item measurements

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11

Check failure on line 123 in src/app/utils/dom.ts

View workflow job for this annotation

GitHub Actions / Tests

src/app/features/room/RoomTimeline.test.tsx > RoomTimeline content ResizeObserver > re-pins to the bottom when the VList content grows while pinned and live

TypeError: scrollEl.scrollTo is not a function ❯ scrollToBottom src/app/utils/dom.ts:123:11 ❯ src/app/features/room/RoomTimeline.tsx:499:9 ❯ src/app/features/room/RoomTimeline.tsx:732:7 ❯ Object.react_stack_bottom_frame node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20 ❯ runWithFiberInDEV node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:874:13 ❯ commitHookEffectListMount node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29 ❯ commitHookLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13213:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:13951:13 ❯ recursivelyTraverseLayoutEffects node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:15159:11 ❯ commitLayoutEffectOnFiber node_modules/.pnpm/react-dom@19.2.8_react@19.2.8/node_modules/react-dom/cjs/react-dom-client.development.js:14031:11
const context = canvas.getContext('2d');
if (!context) {
resolve(undefined);
Expand All @@ -135,7 +135,7 @@

export const scrollToBottom = (scrollEl: HTMLElement, behavior?: 'auto' | 'instant' | 'smooth') => {
scrollEl.scrollTo({
top: Math.round(scrollEl.scrollHeight - scrollEl.offsetHeight),
top: Math.round(scrollEl.scrollHeight - scrollEl.clientHeight),
behavior,
});
};
Expand Down
Loading