diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f93d010..c2f434e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 2.3.0 + +- Add optional `rowKey` prop to `List` and add optional `rowKey`/`columnKey` prop to `Grid`. + ## 2.2.7 - Fixed a problem with project logo not displaying correctly in the README for the Firefox browser. diff --git a/README.md b/README.md index bebfafbd..862c83c5 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,17 @@ This may be used to (re)scroll a row into view.

overscanCount

How many additional rows to render outside of the visible area. This can reduce visual flickering near the edges of a list when scrolling.

+ + + + rowKey +

Lists use the row index as a key by default. +This prop can provide a custom key value.

+

ℹ️ Custom keys can ensure better UX for sortable or filterable lists, +particularly if your row components are stateful. +Refer to the React documentation for more info.

+

⚠️ This prop cannot be auto-memoized because it is called during render. +It is important to always useCallback for this prop; do not use an inline function.

@@ -267,6 +278,17 @@ The grid of cells will fill the height and width defined by this style.

children

Additional content to be rendered within the grid (above cells). This property can be used to render things like overlays or tooltips.

+ + + + columnKey +

Grids use the column index as a key by default. +This prop can be used along with the rowKey prop to provide a custom key value.

+

ℹ️ Custom keys can ensure better UX for sortable or filterable grids, +particularly if your cell components are stateful. +Refer to the React documentation for more info.

+

⚠️ This prop cannot be auto-memoized because it is called during render. +It is important to always useCallback for this prop; do not use an inline function.

@@ -302,6 +324,17 @@ This may be used to (re)scroll a cell into view.

overscanCount

How many additional rows/columns to render outside of the visible area. This can reduce visual flickering near the edges of a grid when scrolling.

+ + + + rowKey +

Grids use the row index as a key by default. +This prop can be used along with the columnKey prop to provide a custom key value.

+

ℹ️ Custom keys can ensure better UX for sortable or filterable grids, +particularly if your cell components are stateful. +Refer to the React documentation for more info.

+

⚠️ This prop cannot be auto-memoized because it is called during render. +It is important to always useCallback for this prop; do not use an inline function.

diff --git a/lib/components/grid/Grid.test.tsx b/lib/components/grid/Grid.test.tsx index c98732ae..9417c382 100644 --- a/lib/components/grid/Grid.test.tsx +++ b/lib/components/grid/Grid.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from "@testing-library/react"; -import { createRef, useLayoutEffect } from "react"; +import { act, createRef, useEffect, useLayoutEffect, useRef } from "react"; import { beforeEach, describe, expect, test, vi } from "vitest"; import { EMPTY_OBJECT } from "../../../src/constants"; import { @@ -76,6 +76,147 @@ describe("Grid", () => { expect(items).toHaveLength(24); }); + describe("custom keys", () => { + test("are called for every rendered column and row", () => { + const columnKey = vi.fn(({ columnIndex }) => columnIndex); + const rowKey = vi.fn(({ rowIndex }) => rowIndex); + + render( + + ); + + for (let index = 0; index < 4; index++) { + expect(columnKey).toHaveBeenCalledWith({ + columnIndex: index, + data: EMPTY_OBJECT, + rowIndex: 0 + }); + expect(columnKey).toHaveBeenCalledWith({ + columnIndex: index, + data: EMPTY_OBJECT, + rowIndex: 1 + }); + } + for (let index = 0; index < 2; index++) { + expect(rowKey).toHaveBeenCalledWith({ + data: EMPTY_OBJECT, + rowIndex: index + }); + } + }); + + test("preserves state when list order changes", async () => { + let data = [ + [1, 2, 3], + [4, 5, 6] + ]; + + const columnKey = vi.fn( + ({ columnIndex, rowIndex }) => data[rowIndex][columnIndex] + ); + const rowKey = vi.fn(({ rowIndex }) => data[rowIndex][0]); + const renderLog: string[] = []; + + function LocalCellComponent({ + columnIndex, + data, + rowIndex, + style + }: CellComponentProps<{ data: number[][] }>) { + const id = data[rowIndex][columnIndex]; + + const idDuringMountRef = useRef(id); + + useEffect(() => { + if (idDuringMountRef.current === id) { + renderLog.push( + `row: ${rowIndex}, column: ${columnIndex}, id: ${id}` + ); + return; + } + + throw Error( + `Expected id "${idDuringMountRef.current}" but was "${id}"` + ); + }); + + return
; + } + + const { rerender } = render( + + ); + + expect(columnKey).toHaveBeenCalled(); + expect(rowKey).toHaveBeenCalled(); + expect(renderLog).toMatchInlineSnapshot(` + [ + "row: 0, column: 0, id: 1", + "row: 0, column: 1, id: 2", + "row: 0, column: 2, id: 3", + "row: 1, column: 0, id: 4", + "row: 1, column: 1, id: 5", + "row: 1, column: 2, id: 6", + ] + `); + + renderLog.splice(0); + columnKey.mockReset(); + rowKey.mockReset(); + + await act(async () => { + data = [...data.reverse()]; + + rerender( + + ); + }); + + expect(columnKey).toHaveBeenCalled(); + expect(rowKey).toHaveBeenCalled(); + expect(renderLog).toMatchInlineSnapshot(` + [ + "row: 0, column: 0, id: 4", + "row: 0, column: 1, id: 5", + "row: 0, column: 2, id: 6", + "row: 1, column: 0, id: 1", + "row: 1, column: 1, id: 2", + "row: 1, column: 2, id: 3", + ] + `); + }); + }); + describe("cell sizes", () => { test("type: number (px)", () => { const { container } = render( diff --git a/lib/components/grid/Grid.tsx b/lib/components/grid/Grid.tsx index 1e19e92a..d31c03d2 100644 --- a/lib/components/grid/Grid.tsx +++ b/lib/components/grid/Grid.tsx @@ -32,6 +32,7 @@ export function Grid< children, className, columnCount, + columnKey, columnWidth, defaultHeight = 0, defaultWidth = 0, @@ -42,6 +43,7 @@ export function Grid< overscanCount = 3, rowCount, rowHeight, + rowKey, style, tagName = "div" as TagName, ...rest @@ -248,7 +250,15 @@ export function Grid< role: "gridcell" }} columnIndex={columnIndex} - key={columnIndex} + key={ + columnKey + ? columnKey({ + columnIndex, + data: cellProps, + rowIndex + }) + : columnIndex + } rowIndex={rowIndex} style={{ position: "absolute", @@ -263,7 +273,18 @@ export function Grid< } children.push( -
+
{columns}
); @@ -274,12 +295,14 @@ export function Grid< CellComponent, cellProps, columnCount, + columnKey, columnStartIndexOverscan, columnStopIndexOverscan, getColumnBounds, getRowBounds, isRtl, rowCount, + rowKey, rowStartIndexOverscan, rowStopIndexOverscan ]); diff --git a/lib/components/grid/types.ts b/lib/components/grid/types.ts index 395053c7..2c9e09e6 100644 --- a/lib/components/grid/types.ts +++ b/lib/components/grid/types.ts @@ -61,6 +61,23 @@ export type GridProps< */ columnCount: number; + /** + * Grids use the column index as a `key` by default. + * This prop can be used along with the `rowKey` prop to provide a custom `key` value. + * + * ℹ️ Custom keys can ensure better UX for sortable or filterable grids, + * particularly if your cell components are stateful. + * Refer to the [React documentation](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) for more info. + * + * ⚠️ This prop cannot be auto-memoized because it is called during render. + * It is important to always `useCallback` for this prop; do not use an inline function. + */ + columnKey?: (args: { + columnIndex: number; + data: CellProps; + rowIndex: number; + }) => React.Key; + /** * Column width; the following formats are supported: * - number of pixels (number) @@ -201,6 +218,19 @@ export type GridProps< | string | ((index: number, cellProps: CellProps) => number); + /** + * Grids use the row index as a `key` by default. + * This prop can be used along with the `columnKey` prop to provide a custom `key` value. + * + * ℹ️ Custom keys can ensure better UX for sortable or filterable grids, + * particularly if your cell components are stateful. + * Refer to the [React documentation](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) for more info. + * + * ⚠️ This prop cannot be auto-memoized because it is called during render. + * It is important to always `useCallback` for this prop; do not use an inline function. + */ + rowKey?: (args: { data: CellProps; rowIndex: number }) => React.Key; + /** * Optional CSS properties. * The grid of cells will fill the height and width defined by this style. diff --git a/lib/components/list/List.test.tsx b/lib/components/list/List.test.tsx index f226209b..f4424924 100644 --- a/lib/components/list/List.test.tsx +++ b/lib/components/list/List.test.tsx @@ -1,5 +1,5 @@ import { act, render, screen } from "@testing-library/react"; -import { createRef, useLayoutEffect } from "react"; +import { createRef, useEffect, useLayoutEffect, useRef } from "react"; import { beforeEach, describe, expect, test, vi } from "vitest"; import { EMPTY_OBJECT } from "../../../src/constants"; import { assert } from "../../utils/assert"; @@ -865,6 +865,117 @@ describe("List", () => { }); }); + describe("rowKey", () => { + test("is used for every rendered row", () => { + const rowKey = vi.fn((index) => index); + + render( + + ); + + const items = screen.queryAllByRole("listitem"); + expect(items).toHaveLength(7); + expect(rowKey).toHaveBeenCalledWith(0, EMPTY_OBJECT); + expect(rowKey).toHaveBeenCalledWith(1, EMPTY_OBJECT); + expect(rowKey).toHaveBeenCalledWith(2, EMPTY_OBJECT); + expect(rowKey).toHaveBeenCalledWith(3, EMPTY_OBJECT); + expect(rowKey).toHaveBeenCalledWith(4, EMPTY_OBJECT); + expect(rowKey).toHaveBeenCalledWith(5, EMPTY_OBJECT); + expect(rowKey).toHaveBeenCalledWith(6, EMPTY_OBJECT); + }); + + test("preserves state when list order changes", async () => { + let data = new Array(5).fill(true).map((_, index) => index); + + const rowKey = vi.fn((index) => data[index]); + const renderLog: string[] = []; + + function LocalRowComponent({ + data, + index, + style + }: RowComponentProps<{ data: number[] }>) { + const id = data[index]; + + const idDuringMountRef = useRef(id); + + useEffect(() => { + if (idDuringMountRef.current === id) { + renderLog.push(`index: ${index}, id: ${id}`); + return; + } + + throw Error( + `Expected id "${idDuringMountRef.current}" but was "${id}"` + ); + }); + + return ( +
+ Index {index}, id {id} +
+ ); + } + + const { rerender } = render( + + ); + + const items = screen.queryAllByRole("listitem"); + expect(items).toHaveLength(5); + expect(rowKey).toHaveBeenCalled(); + expect(renderLog).toMatchInlineSnapshot(` + [ + "index: 0, id: 0", + "index: 1, id: 1", + "index: 2, id: 2", + "index: 3, id: 3", + "index: 4, id: 4", + ] + `); + + renderLog.splice(0); + rowKey.mockReset(); + + await act(async () => { + data = [...data.reverse()]; + + rerender( + + ); + }); + + expect(rowKey).toHaveBeenCalled(); + expect(renderLog).toMatchInlineSnapshot(` + [ + "index: 0, id: 4", + "index: 1, id: 3", + "index: 2, id: 2", + "index: 3, id: 1", + "index: 4, id: 0", + ] + `); + }); + }); + describe("aria attributes", () => { test("should be set by default", () => { render( diff --git a/lib/components/list/List.tsx b/lib/components/list/List.tsx index ae150296..00903077 100644 --- a/lib/components/list/List.tsx +++ b/lib/components/list/List.tsx @@ -37,6 +37,7 @@ export function List< rowComponent: RowComponentProp, rowCount, rowHeight: rowHeightProp, + rowKey, rowProps: rowPropsUnstable, tagName = "div" as TagName, style, @@ -185,7 +186,7 @@ export function List< "aria-setsize": rowCount, role: "listitem" }} - key={index} + key={rowKey ? rowKey(index, rowProps) : index} index={index} style={{ position: "absolute", @@ -206,6 +207,7 @@ export function List< getCellBounds, isDynamicRowHeight, rowCount, + rowKey, rowProps, startIndexOverscan, stopIndexOverscan diff --git a/lib/components/list/types.ts b/lib/components/list/types.ts index ebcbbc1a..92fcc18d 100644 --- a/lib/components/list/types.ts +++ b/lib/components/list/types.ts @@ -134,6 +134,19 @@ export type ListProps< | ((index: number, cellProps: RowProps) => number) | DynamicRowHeight; + /** + * Lists use the row index as a `key` by default. + * This prop can provide a custom `key` value. + * + * ℹ️ Custom keys can ensure better UX for sortable or filterable lists, + * particularly if your row components are stateful. + * Refer to the [React documentation](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key) for more info. + * + * ⚠️ This prop cannot be auto-memoized because it is called during render. + * It is important to always `useCallback` for this prop; do not use an inline function. + */ + rowKey?: (index: number, data: RowProps) => React.Key; + /** * Additional props to be passed to the row-rendering component. * List will automatically re-render rows when values in this object change. diff --git a/lib/hooks/useStableCallback.ts b/lib/hooks/useStableCallback.ts index 954cbb1d..d009f388 100644 --- a/lib/hooks/useStableCallback.ts +++ b/lib/hooks/useStableCallback.ts @@ -2,18 +2,25 @@ import { useCallback, useRef } from "react"; import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect"; // Forked from useEventCallback (usehooks-ts) -export function useStableCallback( - fn: (args: Args) => Return -): (args: Args) => Return { +export function useStableCallback( + fn: (...args: Args) => Return +): (...args: Args) => Return; +export function useStableCallback( + fn: ((...args: Args) => Return) | undefined +): ((...args: Args) => Return) | undefined; +export function useStableCallback( + fn: ((...args: Args) => Return) | undefined +): ((...args: Args) => Return) | undefined { const ref = useRef(() => { - throw new Error("Cannot call during render."); + // This error ensures components don't render with previous/stale function + throw new Error("Cannot call an event handler while rendering."); }); useIsomorphicLayoutEffect(() => { ref.current = fn; }, [fn]); - return useCallback((args: Args) => ref.current?.(args), [ref]) as ( - args: Args + return useCallback((...args: Args) => ref.current?.(...args), [ref]) as ( + ...args: Args ) => Return; } diff --git a/package.json b/package.json index f04cde7e..5fbc4f46 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "react-docgen-typescript": "^2.4.0", "react-dom": "^19.2.3", "react-error-boundary": "^6.0.0", - "react-lib-tools": "0.0.52", + "react-lib-tools": "0.0.53", "react-router-dom": "^7.6.3", "rollup-plugin-terser": "^7.0.2", "rollup-plugin-visualizer": "^6.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61f6e414..b60163a7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -119,8 +119,8 @@ importers: specifier: ^6.0.0 version: 6.0.0(react@19.2.3) react-lib-tools: - specifier: 0.0.52 - version: 0.0.52(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.8.3) + specifier: 0.0.53 + version: 0.0.53(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.8.3) react-router-dom: specifier: ^7.6.3 version: 7.6.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -6664,10 +6664,10 @@ packages: integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== } - react-lib-tools@0.0.52: + react-lib-tools@0.0.53: resolution: { - integrity: sha512-UabOEVqBSNQOV7YJuA+V/Tg2GXDiAzk7ukPqUuPlmvnerX4ri1qZr3t34RdnnD9Y5Ovm29HvuKdw/tImb6qBHg== + integrity: sha512-0KSuuryin8JbehtPtW9D9KMScWKWdE+GwDZC2SZIN9g7/7YzmoXcUxT6v+3GQ1jV3Bh7z+TJcOYY00XaeV5b8w== } peerDependencies: react: ^18.0.0 || ^19.0.0 @@ -12067,7 +12067,7 @@ snapshots: react-is@17.0.2: {} - react-lib-tools@0.0.52(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.8.3): + react-lib-tools@0.0.53(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.8.3): dependencies: "@codemirror/lang-css": 6.3.1 "@codemirror/lang-html": 6.4.11 diff --git a/public/generated/docs/Grid.json b/public/generated/docs/Grid.json index e4858156..d2717ce2 100644 --- a/public/generated/docs/Grid.json +++ b/public/generated/docs/Grid.json @@ -96,6 +96,24 @@ "name": "columnCount", "required": true }, + "columnKey": { + "description": [ + { + "content": "

Grids use the column index as a key by default.\nThis prop can be used along with the rowKey prop to provide a custom key value.

\n" + }, + { + "content": "

Custom keys can ensure better UX for sortable or filterable grids,\nparticularly if your cell components are stateful.\nRefer to the React documentation for more info.

\n", + "intent": "primary" + }, + { + "content": "

This prop cannot be auto-memoized because it is called during render.\nIt is important to always useCallback for this prop; do not use an inline function.

\n", + "intent": "warning" + } + ], + "html": "
columnKey?: (args: { columnIndex: number; data: CellProps; rowIndex: number; }) => Key
", + "name": "columnKey", + "required": false + }, "columnWidth": { "description": [ { @@ -190,6 +208,24 @@ "name": "rowHeight", "required": true }, + "rowKey": { + "description": [ + { + "content": "

Grids use the row index as a key by default.\nThis prop can be used along with the columnKey prop to provide a custom key value.

\n" + }, + { + "content": "

Custom keys can ensure better UX for sortable or filterable grids,\nparticularly if your cell components are stateful.\nRefer to the React documentation for more info.

\n", + "intent": "primary" + }, + { + "content": "

This prop cannot be auto-memoized because it is called during render.\nIt is important to always useCallback for this prop; do not use an inline function.

\n", + "intent": "warning" + } + ], + "html": "
rowKey?: (args: { data: CellProps; rowIndex: number; }) => Key
", + "name": "rowKey", + "required": false + }, "tagName": { "description": [ { diff --git a/public/generated/docs/List.json b/public/generated/docs/List.json index 9b414ca7..6c157eac 100644 --- a/public/generated/docs/List.json +++ b/public/generated/docs/List.json @@ -135,6 +135,24 @@ "name": "rowHeight", "required": true }, + "rowKey": { + "description": [ + { + "content": "

Lists use the row index as a key by default.\nThis prop can provide a custom key value.

\n" + }, + { + "content": "

Custom keys can ensure better UX for sortable or filterable lists,\nparticularly if your row components are stateful.\nRefer to the React documentation for more info.

\n", + "intent": "primary" + }, + { + "content": "

This prop cannot be auto-memoized because it is called during render.\nIt is important to always useCallback for this prop; do not use an inline function.

\n", + "intent": "warning" + } + ], + "html": "
rowKey?: (index: number, data: RowProps) => Key
", + "name": "rowKey", + "required": false + }, "rowProps": { "description": [ { diff --git a/public/generated/site-map.json b/public/generated/site-map.json index 7ef76570..df3e6fd8 100644 --- a/public/generated/site-map.json +++ b/public/generated/site-map.json @@ -47,7 +47,7 @@ { "path": "/list/props", "section": "Lists", - "text": " Renders data with many rows.\n Required props rowComponent: (props: { ariaAttributes: { \"aria-posinset\": number; \"aria-setsize\": number; role: \"listitem\"; }; index: number; style: CSSProperties; } & RowProps) => ReactNode | null React component responsible for rendering a row.\n This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to rowProps.\n The prop types for this component are exported as RowComponentProps \n rowCount: number Number of items to be rendered in the list.\n rowHeight: string | number | DynamicRowHeight | ((index: number, cellProps: RowProps) => number) Row height; the following formats are supported:\n\nnumber of pixels (number)\npercentage of the grid's current height (string)\nfunction that returns the row height (in pixels) given an index and cellProps \ndynamic row height cache returned by the useDynamicRowHeight hook\n\n Dynamic row heights are not as efficient as predetermined sizes.\nIt's recommended to provide your own height values if they can be determined ahead of time.\n rowProps: RowProps Additional props to be passed to the row-rendering component.\nList will automatically re-render rows when values in this object change.\n This object must not contain ariaAttributes, index, or style props.\n Optional props children?: ReactNode Additional content to be rendered within the list (above cells).\nThis property can be used to render things like overlays or tooltips.\n className?: string CSS class name.\n defaultHeight?: number = 0 Default height of list for initial render.\nThis value is important for server rendering.\n listRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToRow(config: { align?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; behavior?: \"auto\" | \"instant\" | \"smooth\"; index: number; }): void; }> Ref used to interact with this component's imperative API.\n This API has imperative methods for scrolling and a getter for the outermost DOM element.\n The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.\n onResize?: (size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void Callback notified when the List's outermost HTMLElement resizes.\nThis may be used to (re)scroll a row into view.\n onRowsRendered?: (visibleRows: { startIndex: number; stopIndex: number; }, allRows: { startIndex: number; stopIndex: number; }) => void Callback notified when the range of visible rows changes.\n overscanCount?: number = 3 How many additional rows to render outside of the visible area.\nThis can reduce visual flickering near the edges of a list when scrolling.\n style?: CSSProperties Optional CSS properties.\nThe list of rows will fill the height defined by this style.\n tagName?: keyof IntrinsicElements = \"div\" as TagName Can be used to override the root HTML element rendered by the List component.\nThe default value is \"div\", meaning that List renders an HTMLDivElement as its root.\n In most use cases the default ARIA roles are sufficient and this prop is not needed.\n ", + "text": " Renders data with many rows.\n Required props rowComponent: (props: { ariaAttributes: { \"aria-posinset\": number; \"aria-setsize\": number; role: \"listitem\"; }; index: number; style: CSSProperties; } & RowProps) => ReactNode | null React component responsible for rendering a row.\n This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to rowProps.\n The prop types for this component are exported as RowComponentProps \n rowCount: number Number of items to be rendered in the list.\n rowHeight: string | number | DynamicRowHeight | ((index: number, cellProps: RowProps) => number) Row height; the following formats are supported:\n\nnumber of pixels (number)\npercentage of the grid's current height (string)\nfunction that returns the row height (in pixels) given an index and cellProps \ndynamic row height cache returned by the useDynamicRowHeight hook\n\n Dynamic row heights are not as efficient as predetermined sizes.\nIt's recommended to provide your own height values if they can be determined ahead of time.\n rowProps: RowProps Additional props to be passed to the row-rendering component.\nList will automatically re-render rows when values in this object change.\n This object must not contain ariaAttributes, index, or style props.\n Optional props children?: ReactNode Additional content to be rendered within the list (above cells).\nThis property can be used to render things like overlays or tooltips.\n className?: string CSS class name.\n defaultHeight?: number = 0 Default height of list for initial render.\nThis value is important for server rendering.\n listRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToRow(config: { align?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; behavior?: \"auto\" | \"instant\" | \"smooth\"; index: number; }): void; }> Ref used to interact with this component's imperative API.\n This API has imperative methods for scrolling and a getter for the outermost DOM element.\n The useListRef and useListCallbackRef hooks are exported for convenience use in TypeScript projects.\n onResize?: (size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void Callback notified when the List's outermost HTMLElement resizes.\nThis may be used to (re)scroll a row into view.\n onRowsRendered?: (visibleRows: { startIndex: number; stopIndex: number; }, allRows: { startIndex: number; stopIndex: number; }) => void Callback notified when the range of visible rows changes.\n overscanCount?: number = 3 How many additional rows to render outside of the visible area.\nThis can reduce visual flickering near the edges of a list when scrolling.\n rowKey?: (index: number, data: RowProps) => Key Lists use the row index as a key by default.\nThis prop can provide a custom key value.\n Custom keys can ensure better UX for sortable or filterable lists,\nparticularly if your row components are stateful.\nRefer to the React documentation for more info.\n This prop cannot be auto-memoized because it is called during render.\nIt is important to always useCallback for this prop; do not use an inline function.\n style?: CSSProperties Optional CSS properties.\nThe list of rows will fill the height defined by this style.\n tagName?: keyof IntrinsicElements = \"div\" as TagName Can be used to override the root HTML element rendered by the List component.\nThe default value is \"div\", meaning that List renders an HTMLDivElement as its root.\n In most use cases the default ARIA roles are sufficient and this prop is not needed.\n ", "title": "List component props" }, { @@ -89,7 +89,7 @@ { "path": "/grid/props", "section": "Grids", - "text": " Renders data with many rows and columns.\n Unlike List rows, Grid cell sizes must be known ahead of time.\nEither static sizes or something that can be derived (from the data in CellProps) without rendering.\n Required props cellComponent: (props: { ariaAttributes: { \"aria-colindex\": number; role: \"gridcell\"; }; columnIndex: number; rowIndex: number; style: CSSProperties; } & CellProps) => ReactNode | null React component responsible for rendering a cell.\n This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to cellProps.\n The prop types for this component are exported as CellComponentProps \n cellProps: CellProps Additional props to be passed to the cell-rendering component.\nGrid will automatically re-render cells when values in this object change.\n This object must not contain ariaAttributes, columnIndex, rowIndex, or style props.\n columnCount: number Number of columns to be rendered in the grid.\n columnWidth: string | number | ((index: number, cellProps: CellProps) => number) Column width; the following formats are supported:\n\nnumber of pixels (number)\npercentage of the grid's current width (string)\nfunction that returns the column width (in pixels) given an index and cellProps \n\n rowCount: number Number of rows to be rendered in the grid.\n rowHeight: string | number | ((index: number, cellProps: CellProps) => number) Row height; the following formats are supported:\n\nnumber of pixels (number)\npercentage of the grid's current height (string)\nfunction that returns the row height (in pixels) given an index and cellProps \n\n Optional props children?: ReactNode Additional content to be rendered within the grid (above cells).\nThis property can be used to render things like overlays or tooltips.\n className?: string CSS class name.\n defaultHeight?: number = 0 Default height of grid for initial render.\nThis value is important for server rendering.\n defaultWidth?: number = 0 Default width of grid for initial render.\nThis value is important for server rendering.\n dir?: string Indicates the directionality of grid cells.\n See HTML dir global attribute for more information.\n gridRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToCell(config: { behavior?: \"auto\" | \"instant\" | \"smooth\"; columnAlign?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; columnIndex: number; rowAlign?: \"auto\" | ... 3 more ... | \"start\"; rowIndex: number; }): void; scrollToColumn(config: { ...; }): void; scrollToR... Imperative Grid API.\n The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.\n onCellsRendered?: (visibleCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }, allCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }) => void Callback notified when the range of rendered cells changes.\n onResize?: (size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void Callback notified when the Grid's outermost HTMLElement resizes.\nThis may be used to (re)scroll a cell into view.\n overscanCount?: number = 3 How many additional rows/columns to render outside of the visible area.\nThis can reduce visual flickering near the edges of a grid when scrolling.\n style?: CSSProperties Optional CSS properties.\nThe grid of cells will fill the height and width defined by this style.\n tagName?: keyof IntrinsicElements = \"div\" as TagName Can be used to override the root HTML element rendered by the List component.\nThe default value is \"div\", meaning that List renders an HTMLDivElement as its root.\n In most use cases the default ARIA roles are sufficient and this prop is not needed.\n ", + "text": " Renders data with many rows and columns.\n Unlike List rows, Grid cell sizes must be known ahead of time.\nEither static sizes or something that can be derived (from the data in CellProps) without rendering.\n Required props cellComponent: (props: { ariaAttributes: { \"aria-colindex\": number; role: \"gridcell\"; }; columnIndex: number; rowIndex: number; style: CSSProperties; } & CellProps) => ReactNode | null React component responsible for rendering a cell.\n This component will receive an index and style prop by default.\nAdditionally it will receive prop values passed to cellProps.\n The prop types for this component are exported as CellComponentProps \n cellProps: CellProps Additional props to be passed to the cell-rendering component.\nGrid will automatically re-render cells when values in this object change.\n This object must not contain ariaAttributes, columnIndex, rowIndex, or style props.\n columnCount: number Number of columns to be rendered in the grid.\n columnWidth: string | number | ((index: number, cellProps: CellProps) => number) Column width; the following formats are supported:\n\nnumber of pixels (number)\npercentage of the grid's current width (string)\nfunction that returns the column width (in pixels) given an index and cellProps \n\n rowCount: number Number of rows to be rendered in the grid.\n rowHeight: string | number | ((index: number, cellProps: CellProps) => number) Row height; the following formats are supported:\n\nnumber of pixels (number)\npercentage of the grid's current height (string)\nfunction that returns the row height (in pixels) given an index and cellProps \n\n Optional props children?: ReactNode Additional content to be rendered within the grid (above cells).\nThis property can be used to render things like overlays or tooltips.\n className?: string CSS class name.\n columnKey?: (args: { columnIndex: number; data: CellProps; rowIndex: number; }) => Key Grids use the column index as a key by default.\nThis prop can be used along with the rowKey prop to provide a custom key value.\n Custom keys can ensure better UX for sortable or filterable grids,\nparticularly if your cell components are stateful.\nRefer to the React documentation for more info.\n This prop cannot be auto-memoized because it is called during render.\nIt is important to always useCallback for this prop; do not use an inline function.\n defaultHeight?: number = 0 Default height of grid for initial render.\nThis value is important for server rendering.\n defaultWidth?: number = 0 Default width of grid for initial render.\nThis value is important for server rendering.\n dir?: string Indicates the directionality of grid cells.\n See HTML dir global attribute for more information.\n gridRef?: Ref<{ readonly element: HTMLDivElement | null; scrollToCell(config: { behavior?: \"auto\" | \"instant\" | \"smooth\"; columnAlign?: \"auto\" | \"center\" | \"end\" | \"smart\" | \"start\"; columnIndex: number; rowAlign?: \"auto\" | ... 3 more ... | \"start\"; rowIndex: number; }): void; scrollToColumn(config: { ...; }): void; scrollToR... Imperative Grid API.\n The useGridRef and useGridCallbackRef hooks are exported for convenience use in TypeScript projects.\n onCellsRendered?: (visibleCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }, allCells: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number; }) => void Callback notified when the range of rendered cells changes.\n onResize?: (size: { height: number; width: number; }, prevSize: { height: number; width: number; }) => void Callback notified when the Grid's outermost HTMLElement resizes.\nThis may be used to (re)scroll a cell into view.\n overscanCount?: number = 3 How many additional rows/columns to render outside of the visible area.\nThis can reduce visual flickering near the edges of a grid when scrolling.\n rowKey?: (args: { data: CellProps; rowIndex: number; }) => Key Grids use the row index as a key by default.\nThis prop can be used along with the columnKey prop to provide a custom key value.\n Custom keys can ensure better UX for sortable or filterable grids,\nparticularly if your cell components are stateful.\nRefer to the React documentation for more info.\n This prop cannot be auto-memoized because it is called during render.\nIt is important to always useCallback for this prop; do not use an inline function.\n style?: CSSProperties Optional CSS properties.\nThe grid of cells will fill the height and width defined by this style.\n tagName?: keyof IntrinsicElements = \"div\" as TagName Can be used to override the root HTML element rendered by the List component.\nThe default value is \"div\", meaning that List renders an HTMLDivElement as its root.\n In most use cases the default ARIA roles are sufficient and this prop is not needed.\n ", "title": "Grid component props" }, {