diff --git a/lib/components/grid/Grid.test.tsx b/lib/components/grid/Grid.test.tsx index 785ff80a..0cc11134 100644 --- a/lib/components/grid/Grid.test.tsx +++ b/lib/components/grid/Grid.test.tsx @@ -1,10 +1,10 @@ import { render, screen } from "@testing-library/react"; -import { useLayoutEffect } from "react"; +import { createRef, useLayoutEffect } from "react"; import { beforeEach, describe, expect, test, vi } from "vitest"; import { EMPTY_OBJECT } from "../../../src/constants"; import { updateMockResizeObserver } from "../../utils/test/mockResizeObserver"; import { Grid } from "./Grid"; -import type { CellComponentProps } from "./types"; +import type { CellComponentProps, GridImperativeAPI } from "./types"; describe("Grid", () => { let mountedCells: Map> = new Map(); @@ -164,8 +164,83 @@ describe("Grid", () => { // TODO }); - test.skip("should scroll to cells", () => { - // TODO + test("should scroll to cell", () => { + const gridRef = createRef(); + + render( + + ); + expect(HTMLElement.prototype.scrollTo).not.toHaveBeenCalled(); + + gridRef.current?.scrollToCell({ columnIndex: 4, rowIndex: 8 }); + + expect(HTMLElement.prototype.scrollTo).toHaveBeenCalledTimes(1); + expect(HTMLElement.prototype.scrollTo).toHaveBeenLastCalledWith({ + behavior: "auto", + left: 25, + top: 140 + }); + }); + + test("should scroll to column", () => { + const gridRef = createRef(); + + render( + + ); + expect(HTMLElement.prototype.scrollTo).not.toHaveBeenCalled(); + + gridRef.current?.scrollToColumn({ index: 4 }); + + expect(HTMLElement.prototype.scrollTo).toHaveBeenCalledTimes(1); + expect(HTMLElement.prototype.scrollTo).toHaveBeenLastCalledWith({ + behavior: "auto", + left: 25 + }); + }); + + test("should scroll to row", () => { + const gridRef = createRef(); + + render( + + ); + expect(HTMLElement.prototype.scrollTo).not.toHaveBeenCalled(); + + gridRef.current?.scrollToRow({ index: 8 }); + + expect(HTMLElement.prototype.scrollTo).toHaveBeenCalledTimes(1); + expect(HTMLElement.prototype.scrollTo).toHaveBeenLastCalledWith({ + behavior: "auto", + top: 140 + }); }); }); diff --git a/lib/components/grid/Grid.tsx b/lib/components/grid/Grid.tsx index 6dc2f218..10758186 100644 --- a/lib/components/grid/Grid.tsx +++ b/lib/components/grid/Grid.tsx @@ -96,18 +96,24 @@ export function Grid({ rowAlign?: Align; rowIndex: number; }) { - scrollToRowIndex({ - align: rowAlign, - behavior, - containerScrollOffset: element?.scrollTop ?? 0, - index: rowIndex - }); - scrollToColumnIndex({ + const left = scrollToColumnIndex({ align: columnAlign, - behavior, containerScrollOffset: element?.scrollLeft ?? 0, index: columnIndex }); + const top = scrollToRowIndex({ + align: rowAlign, + containerScrollOffset: element?.scrollTop ?? 0, + index: rowIndex + }); + + if (typeof element?.scrollTo === "function") { + element.scrollTo({ + behavior, + left, + top + }); + } }, scrollToColumn({ @@ -119,12 +125,18 @@ export function Grid({ behavior?: ScrollBehavior; index: number; }) { - scrollToColumnIndex({ + const left = scrollToColumnIndex({ align, - behavior, containerScrollOffset: element?.scrollLeft ?? 0, index }); + + if (typeof element?.scrollTo === "function") { + element.scrollTo({ + behavior, + left + }); + } }, scrollToRow({ @@ -136,12 +148,18 @@ export function Grid({ behavior?: ScrollBehavior; index: number; }) { - scrollToRowIndex({ + const top = scrollToRowIndex({ align, - behavior, containerScrollOffset: element?.scrollTop ?? 0, index }); + + if (typeof element?.scrollTo === "function") { + element.scrollTo({ + behavior, + top + }); + } } }), [element, scrollToColumnIndex, scrollToRowIndex] diff --git a/lib/components/list/List.tsx b/lib/components/list/List.tsx index 56ca72e1..ba1db3d1 100644 --- a/lib/components/list/List.tsx +++ b/lib/components/list/List.tsx @@ -67,12 +67,18 @@ export function List({ behavior?: ScrollBehavior; index: number; }) { - scrollToIndex({ + const top = scrollToIndex({ align, - behavior, containerScrollOffset: element?.scrollTop ?? 0, index }); + + if (typeof element?.scrollTo === "function") { + element.scrollTo({ + behavior, + top + }); + } } }), [element, scrollToIndex] diff --git a/lib/core/useVirtualizer.ts b/lib/core/useVirtualizer.ts index fa555f39..ea6dbde3 100644 --- a/lib/core/useVirtualizer.ts +++ b/lib/core/useVirtualizer.ts @@ -194,12 +194,10 @@ export function useVirtualizer({ const scrollToIndex = useStableCallback( ({ align = "auto", - behavior = "auto", containerScrollOffset, index }: { align?: Align; - behavior?: ScrollBehavior; containerScrollOffset: number; index: number; }) => { @@ -221,25 +219,15 @@ export function useVirtualizer({ scrollOffset }); - if (typeof containerElement.scrollTo === "function") { - if (direction === "horizontal") { - containerElement.scrollTo({ - left: scrollOffset, - behavior: behavior || undefined - }); - } else { - containerElement.scrollTo({ - behavior: behavior || undefined, - top: scrollOffset - }); - } - } else { + if (typeof containerElement.scrollTo !== "function") { // Special case for environments like jsdom that don't implement scrollTo const next = getStartStopIndices(scrollOffset); if (next[0] !== startIndex || next[1] !== stopIndex) { setIndices(next); } } + + return scrollOffset; } } ); diff --git a/src/routes/grid/ImperativeApiRoute.tsx b/src/routes/grid/ImperativeApiRoute.tsx index 5cd5c73b..08fa144b 100644 --- a/src/routes/grid/ImperativeApiRoute.tsx +++ b/src/routes/grid/ImperativeApiRoute.tsx @@ -76,7 +76,7 @@ export default function GridImperativeApiRoute() { const gridRef = useGridRef(null); - const scrollToRow = () => { + const scrollToCell = () => { const grid = gridRef.current; if (grid) { const columnIndex = column?.value @@ -158,7 +158,7 @@ export default function GridImperativeApiRoute() {