diff --git a/playwright/e2e/lazy-loading/lazy-loading.spec.ts b/playwright/e2e/lazy-loading/lazy-loading.spec.ts index c8ecc43cdb..f6f098a079 100644 --- a/playwright/e2e/lazy-loading/lazy-loading.spec.ts +++ b/playwright/e2e/lazy-loading/lazy-loading.spec.ts @@ -30,7 +30,7 @@ test.describe("Lazy Loading", () => { }); test.beforeEach(async ({ page, homeserver, user, bot, app }) => { - // The charlies were running off the bottom of the screen. + // The charlies were running off the bottom of the screen. // We no longer overscan the member list so the result is they are not in the dom. // Increase the viewport size to ensure they are. await page.setViewportSize({ width: 1000, height: 1000 }); diff --git a/src/components/utils/ListView.tsx b/src/components/utils/ListView.tsx index d61f2d91ff..bea53eb594 100644 --- a/src/components/utils/ListView.tsx +++ b/src/components/utils/ListView.tsx @@ -89,6 +89,8 @@ export function ListView(props: IListViewProps(false); + // Update the key-to-index mapping whenever items change React.useEffect(() => { const newKeyToIndexMap = new Map(); @@ -125,15 +127,21 @@ export function ListView(props: IListViewProps { // Ensure index is within bounds const clampedIndex = Math.max(0, Math.min(index, items.length - 1)); - + if (isScrollingToItem.current) { + // If already scrolling to an item drop this request. Adding further requests + // causes the event to bubble up and be handled by other components(unintentional timeline scrolling was observed). + return; + } if (items[clampedIndex]) { const key = getItemKey(items[clampedIndex]); + setfocusKey(key); + isScrollingToItem.current = true; virtuosoHandleRef?.current?.scrollIntoView({ index: clampedIndex, align: align, behavior: "auto", done: () => { - setfocusKey(key); + isScrollingToItem.current = false; }, }); } @@ -195,11 +203,11 @@ export function ListView(props: IListViewProps { expect(items[lastIndex]).toHaveAttribute("tabindex", "-1"); }); - it("should prevent default and stop propagation for handled keys", () => { - render(); - const container = screen.getByRole("grid"); - - // Focus the container first to establish initial focus - fireEvent.focus(container); - - // Create a spy to monitor the event - const keyDownSpy = jest.fn(); - container.addEventListener("keydown", keyDownSpy); - - fireEvent.keyDown(container, { code: "ArrowDown" }); - - // Check that the event was prevented and stopped - expect(keyDownSpy).toHaveBeenCalled(); - const event = keyDownSpy.mock.calls[0][0]; - expect(event.defaultPrevented).toBe(true); - }); - - it("should not prevent default for unhandled keys", () => { - render(); - const container = screen.getByRole("grid"); - const event = new KeyboardEvent("keydown", { code: "KeyA", bubbles: true, cancelable: true }); - const preventDefault = jest.spyOn(event, "preventDefault"); - const stopPropagation = jest.spyOn(event, "stopPropagation"); - - fireEvent(container, event); - - expect(preventDefault).not.toHaveBeenCalled(); - expect(stopPropagation).not.toHaveBeenCalled(); - }); - it("should skip non-focusable items when navigating down", async () => { // Create items where every other item is not focusable const mixedItems = [