Skip to content
Merged
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
83 changes: 79 additions & 4 deletions lib/components/grid/Grid.test.tsx
Original file line number Diff line number Diff line change
@@ -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<string, CellComponentProps<object>> = new Map();
Expand Down Expand Up @@ -164,8 +164,83 @@ describe("Grid", () => {
// TODO
});

test.skip("should scroll to cells", () => {
// TODO
test("should scroll to cell", () => {
const gridRef = createRef<GridImperativeAPI>();

render(
<Grid
cellComponent={CellComponent}
cellProps={EMPTY_OBJECT}
columnCount={25}
columnWidth={25}
gridRef={gridRef}
overscanCount={0}
rowCount={25}
rowHeight={20}
/>
);
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<GridImperativeAPI>();

render(
<Grid
cellComponent={CellComponent}
cellProps={EMPTY_OBJECT}
columnCount={25}
columnWidth={25}
gridRef={gridRef}
overscanCount={0}
rowCount={25}
rowHeight={20}
/>
);
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<GridImperativeAPI>();

render(
<Grid
cellComponent={CellComponent}
cellProps={EMPTY_OBJECT}
columnCount={25}
columnWidth={25}
gridRef={gridRef}
overscanCount={0}
rowCount={25}
rowHeight={20}
/>
);
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
});
});
});

Expand Down
42 changes: 30 additions & 12 deletions lib/components/grid/Grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,24 @@ export function Grid<CellProps extends object>({
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({
Expand All @@ -119,12 +125,18 @@ export function Grid<CellProps extends object>({
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({
Expand All @@ -136,12 +148,18 @@ export function Grid<CellProps extends object>({
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]
Expand Down
10 changes: 8 additions & 2 deletions lib/components/list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,18 @@ export function List<RowProps extends object>({
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]
Expand Down
18 changes: 3 additions & 15 deletions lib/core/useVirtualizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,10 @@ export function useVirtualizer<Props extends object>({
const scrollToIndex = useStableCallback(
({
align = "auto",
behavior = "auto",
containerScrollOffset,
index
}: {
align?: Align;
behavior?: ScrollBehavior;
containerScrollOffset: number;
index: number;
}) => {
Expand All @@ -221,25 +219,15 @@ export function useVirtualizer<Props extends object>({
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;
}
}
);
Expand Down
4 changes: 2 additions & 2 deletions src/routes/grid/ImperativeApiRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -158,7 +158,7 @@ export default function GridImperativeApiRoute() {
<Button
className="shrink-0"
disabled={!column.value && !title.value}
onClick={scrollToRow}
onClick={scrollToCell}
>
Scroll
</Button>
Expand Down