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
51 changes: 51 additions & 0 deletions lib/components/grid/Grid.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,57 @@ describe("Grid", () => {
top: 140
});
});

test("should throw a meaningful error if an invalid index is passed to scrollToRow", () => {
const gridRef = createRef<GridImperativeAPI>();

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

expect(() => {
gridRef.current?.scrollToRow({ index: -1 });
}).toThrowError("Invalid index specified: -1");

expect(() => {
gridRef.current?.scrollToRow({ index: 25 });
}).toThrowError("Invalid index specified: 25");

expect(() => {
gridRef.current?.scrollToColumn({ index: -1 });
}).toThrowError("Invalid index specified: -1");

expect(() => {
gridRef.current?.scrollToColumn({ index: 25 });
}).toThrowError("Invalid index specified: 25");

expect(() => {
gridRef.current?.scrollToCell({ columnIndex: -1, rowIndex: 0 });
}).toThrowError("Invalid index specified: -1");

expect(() => {
gridRef.current?.scrollToCell({ columnIndex: 25, rowIndex: 0 });
}).toThrowError("Invalid index specified: 25");

expect(() => {
gridRef.current?.scrollToCell({ columnIndex: 0, rowIndex: -1 });
}).toThrowError("Invalid index specified: -1");

expect(() => {
gridRef.current?.scrollToCell({ columnIndex: 0, rowIndex: 25 });
}).toThrowError("Invalid index specified: 25");

expect(HTMLElement.prototype.scrollTo).not.toHaveBeenCalled();
});
});

test("should auto-memoize cellProps object using shallow equality", () => {
Expand Down
71 changes: 71 additions & 0 deletions lib/components/grid/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,22 @@ export type GridProps<
* ℹ️ The `useGridRef` and `useGridCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
gridRef?: Ref<{
/**
* Outermost HTML element for the grid if mounted and null (if not mounted.
*/
get element(): HTMLDivElement | null;

/**
* Scrolls the grid so that the specified row and column are visible.
*
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param columnAlign Determines the horizontal alignment of the element within the list
* @param columnIndex Index of the column to scroll to (0-based)
* @param rowAlign Determines the vertical alignment of the element within the list
* @param rowIndex Index of the row to scroll to (0-based)
*
* @throws RangeError if an invalid row or column index is provided
*/
scrollToCell(config: {
behavior?: "auto" | "instant" | "smooth";
columnAlign?: "auto" | "center" | "end" | "smart" | "start";
Expand All @@ -108,12 +122,30 @@ export type GridProps<
rowIndex: number;
}): void;

/**
* Scrolls the grid so that the specified column is visible.
*
* @param align Determines the horizontal alignment of the element within the list
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param index Index of the column to scroll to (0-based)
*
* @throws RangeError if an invalid column index is provided
*/
scrollToColumn(config: {
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
index: number;
}): void;

/**
* Scrolls the grid so that the specified row is visible.
*
* @param align Determines the vertical alignment of the element within the list
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param index Index of the row to scroll to (0-based)
*
* @throws RangeError if an invalid row index is provided
*/
scrollToRow(config: {
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
Expand Down Expand Up @@ -205,9 +237,30 @@ export type CachedBounds = Map<
}
>;

/**
* Ref used to interact with this component's imperative API.
*
* This API has imperative methods for scrolling and a getter for the outermost DOM element.
*
* ℹ️ The `useGridRef` and `useGridCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
export type GridImperativeAPI = {
/**
* Outermost HTML element for the grid if mounted and null (if not mounted.
*/
get element(): HTMLDivElement | null;

/**
* Scrolls the grid so that the specified row and column are visible.
*
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param columnAlign Determines the horizontal alignment of the element within the list
* @param columnIndex Index of the column to scroll to (0-based)
* @param rowAlign Determines the vertical alignment of the element within the list
* @param rowIndex Index of the row to scroll to (0-based)
*
* @throws RangeError if an invalid row or column index is provided
*/
scrollToCell({
behavior,
columnAlign,
Expand All @@ -222,6 +275,15 @@ export type GridImperativeAPI = {
rowIndex: number;
}): void;

/**
* Scrolls the grid so that the specified column is visible.
*
* @param align Determines the horizontal alignment of the element within the list
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param index Index of the column to scroll to (0-based)
*
* @throws RangeError if an invalid column index is provided
*/
scrollToColumn({
align,
behavior,
Expand All @@ -232,6 +294,15 @@ export type GridImperativeAPI = {
index: number;
}): void;

/**
* Scrolls the grid so that the specified row is visible.
*
* @param align Determines the vertical alignment of the element within the list
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param index Index of the row to scroll to (0-based)
*
* @throws RangeError if an invalid row index is provided
*/
scrollToRow({
align,
behavior,
Expand Down
24 changes: 24 additions & 0 deletions lib/components/list/List.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,30 @@ describe("List", () => {
top: 125
});
});

test("should throw a meaningful error if an invalid index is passed to scrollToRow", () => {
const listRef = createRef<ListImperativeAPI>();

render(
<List
rowCount={25}
listRef={listRef}
rowComponent={RowComponent}
rowHeight={25}
rowProps={EMPTY_OBJECT}
/>
);

expect(() => {
listRef.current?.scrollToRow({ index: -1 });
}).toThrowError("Invalid index specified: -1");

expect(() => {
listRef.current?.scrollToRow({ index: 25 });
}).toThrowError("Invalid index specified: 25");

expect(HTMLElement.prototype.scrollTo).not.toHaveBeenCalled();
});
});

test("should auto-memoize rowProps object using shallow equality", () => {
Expand Down
31 changes: 31 additions & 0 deletions lib/components/list/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,20 @@ export type ListProps<
* ℹ️ The `useListRef` and `useListCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
listRef?: Ref<{
/**
* Outermost HTML element for the list if mounted and null (if not mounted.
*/
get element(): HTMLDivElement | null;

/**
* Scrolls the list so that the specified row is visible.
*
* @param align Determines the vertical alignment of the element within the list
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param index Index of the row to scroll to (0-based)
*
* @throws RangeError if an invalid row index is provided
*/
scrollToRow(config: {
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
Expand Down Expand Up @@ -161,9 +173,28 @@ export type CachedBounds = Map<
}
>;

/**
* Ref used to interact with this component's imperative API.
*
* This API has imperative methods for scrolling and a getter for the outermost DOM element.
*
* ℹ️ The `useListRef` and `useListCallbackRef` hooks are exported for convenience use in TypeScript projects.
*/
export type ListImperativeAPI = {
/**
* Outermost HTML element for the list if mounted and null (if not mounted.
*/
get element(): HTMLDivElement | null;

/**
* Scrolls the list so that the specified row is visible.
*
* @param align Determines the vertical alignment of the element within the list
* @param behavior Determines whether scrolling is instant or animates smoothly
* @param index Index of the row to scroll to (0-based)
*
* @throws RangeError if an invalid row index is provided
*/
scrollToRow({
align,
behavior,
Expand Down
6 changes: 6 additions & 0 deletions lib/core/getOffsetForIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ export function getOffsetForIndex<Props extends object>({
containerScrollOffset: number;
containerSize: number;
}) {
if (index < 0 || index >= itemCount) {
throw RangeError(`Invalid index specified: ${index}`, {
cause: `Index ${index} is not within the range of 0 - ${itemCount - 1}`
});
}

const estimatedTotalSize = getEstimatedSize({
cachedBounds,
itemCount,
Expand Down