diff --git a/README.md b/README.md index b91c5cb848..2940542996 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,15 @@ The DataGrid component is designed to handle large datasets efficiently while offering a rich set of features for customization and interactivity. +## Table of contents + +- [Features](#features) +- [Links](#links) +- [Installation](#installation) +- [Getting started](#getting-started) +- [Styling and Customization](#styling-and-customization) +- [API Reference](#api-reference) + ## Features - [React 19.2+](package.json) support @@ -26,7 +35,7 @@ The DataGrid component is designed to handle large datasets efficiently while of - Great performance thanks to virtualization: columns and rows outside the viewport are not rendered - Strictly typed with TypeScript - [Keyboard accessibility](https://comcast.github.io/react-data-grid/#/CommonFeatures) -- Light and dark mode support out of the box. +- Light and dark mode support out of the box via `color-scheme`. - [Frozen columns](https://comcast.github.io/react-data-grid/#/CommonFeatures): Freeze columns to keep them visible during horizontal scrolling. - [Column resizing](https://comcast.github.io/react-data-grid/#/CommonFeatures) - [Multi-column sorting](https://comcast.github.io/react-data-grid/#/CommonFeatures) @@ -51,7 +60,6 @@ The DataGrid component is designed to handle large datasets efficiently while of - [Examples website](https://comcast.github.io/react-data-grid/) - [Source code](website) - [Changelog](CHANGELOG.md) -- [Contributing](CONTRIBUTING.md) ## Installation @@ -127,7 +135,7 @@ The DataGrid provides multiple ways to customize its appearance and behavior. ### Light/Dark Themes -The DataGrid supports both light and dark color schemes out of the box using CSS's `light-dark()` function. The theme automatically adapts based on the user's system preference when `color-scheme: light dark;` is set. +The DataGrid supports both light and dark color schemes out of the box using the `light-dark()` CSS function. The theme automatically adapts based on the user's system preference when `color-scheme: light dark;` is set. To enforce a specific theme, we recommend setting the standard `color-scheme` CSS property on the `:root`: @@ -244,9 +252,9 @@ Replace default components with custom implementations using the [`renderers`](# ##### DataGridProps -###### `columns: readonly Column[]` +###### `columns: readonly ColumnOrColumnGroup[]` -An array of column definitions. Each column should have a key and name. See the [`Column`](#columntrow-tsummaryrow) type for all available options. +An array of column definitions and/or column groups. See the [`ColumnOrColumnGroup`](#columnorcolumngrouptrow-tsummaryrow) type for all available options. :warning: **Performance:** Passing a new `columns` array will trigger a re-render and recalculation for the entire grid. Always memoize this prop using `useMemo` or define it outside the component to avoid unnecessary re-renders. @@ -269,6 +277,10 @@ An array of rows, the rows data can be of any type. setRows(rows.map((row) => ({ ...row }))); ``` +###### `ref?: Maybe>` + +Optional ref for imperative APIs like scrolling/selecting a cell. See [`DataGridHandle`](#datagridhandle). + ###### `topSummaryRows?: Maybe` Rows pinned at the top of the grid for summary purposes. @@ -342,13 +354,13 @@ function getRowHeight(row) { ###### `headerRowHeight?: Maybe` -**Default:** `35` pixels +**Default:** `rowHeight` when it is a number, otherwise `35` pixels Height of the header row in pixels. ###### `summaryRowHeight?: Maybe` -**Default:** `35` pixels +**Default:** `rowHeight` when it is a number, otherwise `35` pixels Height of each summary row in pixels. @@ -399,7 +411,7 @@ Callback triggered when the selection changes. import { useState } from 'react'; import { DataGrid, SelectColumn } from 'react-data-grid'; -const rows: readonly Rows[] = [...]; +const rows: readonly Row[] = [...]; const columns: readonly Column[] = [ SelectColumn, @@ -434,6 +446,8 @@ function MyGrid() { An array of sorted columns. +Sorting is controlled: the grid does not reorder `rows` for you. Apply the sorting to your `rows` state (or derived rows) based on `sortColumns`. + ###### `onSortColumnsChange?: Maybe<(sortColumns: SortColumn[]) => void>` Callback triggered when sorting changes. @@ -442,7 +456,7 @@ Callback triggered when sorting changes. import { useState } from 'react'; import { DataGrid, SelectColumn } from 'react-data-grid'; -const rows: readonly Rows[] = [...]; +const rows: readonly Row[] = [...]; const columns: readonly Column[] = [ { @@ -498,12 +512,12 @@ function MyGrid() { ###### `onFill?: Maybe<(event: FillEvent) => R>` -###### `onCellMouseDown: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` +###### `onCellMouseDown?: CellMouseEventHandler` Callback triggered when a pointer becomes active in a cell. The default behavior is to select the cell. Call `preventGridDefault` to prevent the default behavior. ```tsx -function onCellMouseDown(args: CellMouseDownArgs, event: CellMouseEvent) { +function onCellMouseDown(args: CellMouseArgs, event: CellMouseEvent) { if (args.column.key === 'id') { event.preventGridDefault(); } @@ -512,7 +526,7 @@ function onCellMouseDown(args: CellMouseDownArgs, event: CellMouseEvent) ; ``` -###### `onCellClick?: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` +###### `onCellClick?: CellMouseEventHandler` Callback triggered when a cell is clicked. @@ -536,7 +550,7 @@ function onCellClick(args: CellMouseArgs, event: CellMouseEvent) { } ``` -###### `onCellDoubleClick?: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` +###### `onCellDoubleClick?: CellMouseEventHandler` Callback triggered when a cell is double-clicked. The default behavior is to open the editor if the cell is editable. Call `preventGridDefault` to prevent the default behavior. @@ -550,7 +564,7 @@ function onCellDoubleClick(args: CellMouseArgs, event: CellMouseEvent) { ; ``` -###### `onCellContextMenu?: Maybe<(args: CellMouseArgs, event: CellMouseEvent) => void>` +###### `onCellContextMenu?: CellMouseEventHandler` Callback triggered when a cell is right-clicked. @@ -597,18 +611,20 @@ Check [more examples](website/routes/CellNavigation.tsx) Callback triggered when a cell's content is copied. -###### `onCellPaste?: Maybe<(args: CellPasteArgs, NoInfer>, event: CellClipboardEvent) => void>` +###### `onCellPaste?: Maybe<(args: CellPasteArgs, NoInfer>, event: CellClipboardEvent) => R>` Callback triggered when content is pasted into a cell. -###### `onSelectedCellChange?: Maybe<(args: CellSelectArgs) => void>;` +Return the updated row; the grid will call `onRowsChange` with it. + +###### `onSelectedCellChange?: Maybe<(args: CellSelectArgs) => void>` Triggered when the selected cell is changed. Arguments: - `args.rowIdx`: `number` - row index -- `args.row`: `R` - row object of the currently selected cell +- `args.row`: `R | undefined` - row object of the currently selected cell - `args.column`: `CalculatedColumn` - column object of the currently selected cell ###### `onScroll?: Maybe<(event: React.UIEvent) => void>` @@ -633,15 +649,7 @@ This prop can be used to disable virtualization. Custom renderers for cells, rows, and other components. -```tsx -interface Renderers { - renderCell?: Maybe<(key: Key, props: CellRendererProps) => ReactNode>; - renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>; - renderRow?: Maybe<(key: Key, props: RenderRowProps) => ReactNode>; - renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>; - noRowsFallback?: Maybe; -} -``` +See the [`Renderers`](#rendererstrow-tsummaryrow) type for the full shape. Example of replacing default components: @@ -679,9 +687,13 @@ const customRenderers: Renderers = { The default `` component can be wrapped via the `renderRow` prop to add contexts or tweak props: ```tsx -import { DataGrid, RenderRowProps, Row } from 'react-data-grid'; +import { DataGrid, Row, type RenderRowProps } from 'react-data-grid'; -function myRowRenderer(key: React.Key, props: RenderRowProps) { +interface MyRow { + id: number; +} + +function myRowRenderer(key: React.Key, props: RenderRowProps) { return ( @@ -712,7 +724,7 @@ function MyGrid() { :warning: **Performance:** Define this function outside your component or memoize it with `useCallback` to avoid re-rendering all rows on every render. -###### `headerRowClass?: Maybe>` +###### `headerRowClass?: Maybe` Custom class name for the header row. @@ -737,6 +749,10 @@ Custom class name for the grid. Custom styles for the grid. +###### `role?: string | undefined` + +ARIA role for the grid container. Defaults to `grid`. + ###### `'aria-label'?: string | undefined` The label of the grid. We recommend providing a label using `aria-label` or `aria-labelledby` @@ -745,6 +761,10 @@ The label of the grid. We recommend providing a label using `aria-label` or `ari The id of the element containing a label for the grid. We recommend providing a label using `aria-label` or `aria-labelledby` +###### `'aria-rowcount'?: number | undefined` + +Total number of rows for assistive technologies. + ###### `'aria-description'?: string | undefined` ###### `'aria-describedby'?: string | undefined` @@ -753,7 +773,7 @@ If the grid has a caption or description, `aria-describedby` can be set on the g ###### `'data-testid'?: Maybe` -This prop can be used to add a testid for testing. We recommend querying the grid by by its `role` and `name`. +This prop can be used to add a testid for testing. We recommend querying the grid by its `role` and `name`. ```tsx function MyGrid() { @@ -766,6 +786,10 @@ test('grid', async () => { }); ``` +###### `'data-cy'?: Maybe` + +Optional attribute to help with Cypress (or similar) selectors. + #### `` `TreeDataGrid` is a component built on top of `DataGrid` to add hierarchical row grouping. This implements the [Treegrid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treegrid/). @@ -789,16 +813,19 @@ The following `DataGrid` props are not supported in `TreeDataGrid`: - `onFill` - Drag-fill is disabled for tree grids - `isRowSelectionDisabled` - Row selection disabling is not available +- `role` - `TreeDataGrid` manages the ARIA role +- `aria-rowcount` - `TreeDataGrid` manages the ARIA row count **Caveats:** - Group columns cannot be rendered under one column - Group columns are automatically frozen and cannot be unfrozen - Cell copy/paste does not work on group rows +- Column groups are not supported; `columns` must be `Column[]` ##### TreeDataGridProps -All [`DataGridProps`](#datagridprops) are supported except those listed above, plus the following additional props: +All [`DataGridProps`](#datagridprops) are supported except those listed above. The `columns` prop only supports `Column[]` (no column groups). When `rowHeight` is a function, it receives [`RowHeightArgs`](#rowheightargstrow) instead of just the row. Additional props are listed below: ###### `groupBy: readonly string[]` @@ -837,7 +864,7 @@ function MyGrid() { **Required.** A function that groups rows by the specified column key. Returns an object where keys are the group values and values are arrays of rows belonging to that group. ```tsx -function rowGrouper(rows: Row[], columnKey: string) { +function rowGrouper(rows: readonly Row[], columnKey: string): Record { return Object.groupBy(rows, (row) => row[columnKey]); } ``` @@ -912,22 +939,22 @@ A formatter component for rendering row selection checkboxes. Whether the checkbox is checked. -###### `tabIndex: number` +###### `tabIndex?: number | undefined` The tab index for keyboard navigation. +###### `indeterminate?: boolean | undefined` + +Whether the checkbox is in an indeterminate state. + ###### `disabled?: boolean | undefined` Whether the checkbox is disabled. -###### `onChange: (value: boolean, isShiftClick: boolean) => void` +###### `onChange: (checked: boolean, shift: boolean) => void` Callback when the checkbox state changes. -###### `onClick?: MouseEventHandler | undefined` - -Optional click handler. - ###### `'aria-label'?: string | undefined` Accessible label for the checkbox. @@ -936,6 +963,10 @@ Accessible label for the checkbox. ID of the element that labels the checkbox. +#### `` + +Low-level component used by `renderToggleGroup` to render the expand/collapse control. Useful if you build a custom group cell renderer. + ### Hooks #### `useHeaderRowSelection()` @@ -951,14 +982,23 @@ Hook for managing header row selection state. Used within custom header cell ren **Example:** ```tsx +import { useLayoutEffect, useRef } from 'react'; + function CustomHeaderCell() { const { isIndeterminate, isRowSelected, onRowSelectionChange } = useHeaderRowSelection(); + const checkboxRef = useRef(null); + + useLayoutEffect(() => { + if (checkboxRef.current) { + checkboxRef.current.indeterminate = isIndeterminate && !isRowSelected; + } + }, [isIndeterminate, isRowSelected]); return ( onRowSelectionChange({ checked: event.target.checked })} /> ); @@ -989,7 +1029,7 @@ function CustomSelectCell({ row }: RenderCellProps) { onChange={(event) => onRowSelectionChange({ row, - checked: event.target.checked, + checked: event.currentTarget.checked, isShiftClick: event.nativeEvent.shiftKey }) } @@ -1128,12 +1168,25 @@ Context for providing default renderers to DataGrids in your app. **Example:** ```tsx -import { DataGridDefaultRenderersContext, type Renderers } from 'react-data-grid'; +import { + DataGridDefaultRenderersContext, + renderCheckbox, + renderSortIcon, + renderSortPriority, + type Renderers +} from 'react-data-grid'; // custom implementations of renderers const defaultGridRenderers: Renderers = { renderCheckbox, - renderSortStatus + renderSortStatus(props) { + return ( + <> + {renderSortIcon(props)} + {renderSortPriority(props)} + + ); + } }; function AppProvider({ children }) { @@ -1318,7 +1371,7 @@ Render function to render the content of edit cells. When set, the column is aut Control whether cells can be edited with `renderEditCell`. -##### `colSpan?: Maybe<(args: ColSpanArgs) => Maybe>` +##### `colSpan?: Maybe<(args: ColSpanArgs) => Maybe>` Function to determine how many columns this cell should span. Returns the number of columns to span, or `undefined` for no spanning. See the `ColSpanArgs` type in the Types section below. @@ -1345,7 +1398,7 @@ const columns: readonly Column[] = [ **Default**: `false` -Determines whether column is frozen. Frozen columns are pinned on the left. At the moment we do not support pinning columns on the right. +Determines whether column is frozen. Frozen columns are pinned to the start edge (left in LTR, right in RTL). Per-column pinning to the end edge is not supported at the moment. ##### `resizable?: Maybe` @@ -1461,7 +1514,7 @@ type CalculatedColumnOrColumnGroup = CalculatedColumnParent | Calc #### `RowHeightArgs` -Arguments passed to the `rowHeight` function when it's a function. +Arguments passed to `TreeDataGrid`'s `rowHeight` prop when it is a function. ```tsx type RowHeightArgs = { type: 'ROW'; row: TRow } | { type: 'GROUP'; row: GroupRow }; @@ -1477,7 +1530,7 @@ function getRowHeight(args: RowHeightArgs): number { return args.row.isLarge ? 60 : 35; } - + ``` #### `RenderCellProps` @@ -1608,7 +1661,7 @@ interface RenderRowProps { Props passed to the cell renderer when using `renderers.renderCell`. -Extends `RenderRowProps` with cell-specific properties like `column`, `colSpan`, `isCellSelected`, etc. +Shares a base type with row render props (DOM props and cell event handlers) but only includes cell-specific fields like `column`, `row`, `rowIdx`, `colSpan`, and selection state. #### `Renderers` @@ -1664,6 +1717,16 @@ function onCellClick(args: CellMouseArgs, event: CellMouseEvent) { } ``` +#### `CellMouseEventHandler` (internal) + +Handler type used by `onCellMouseDown`, `onCellClick`, `onCellDoubleClick`, and `onCellContextMenu`. This helper type is not exported; the shape is shown for reference. + +```tsx +type CellMouseEventHandler = Maybe< + (args: CellMouseArgs, event: CellMouseEvent) => void +>; +``` + #### `CellMouseEvent` Extends `React.MouseEvent` with grid-specific methods. @@ -1881,9 +1944,9 @@ interface FillEvent { Used with the `onFill` prop to handle cell value dragging. -#### `GroupRow` +#### `GroupRow` (internal) -Represents a grouped row in `TreeDataGrid`. +Represents a grouped row in `TreeDataGrid`. This helper type is not exported; the shape is shown for reference. ```tsx interface GroupRow { @@ -2019,7 +2082,14 @@ Default options applied to all columns. ```tsx type DefaultColumnOptions = Pick< Column, - 'minWidth' | 'maxWidth' | 'resizable' | 'sortable' | 'draggable' + | 'renderCell' + | 'renderHeaderCell' + | 'width' + | 'minWidth' + | 'maxWidth' + | 'resizable' + | 'sortable' + | 'draggable' >; ``` @@ -2031,9 +2101,9 @@ Grid layout bidirectionality. type Direction = 'ltr' | 'rtl'; ``` -#### `Maybe` +#### `Maybe` (internal) -Utility type for optional values. +Utility type for optional values. This helper type is not exported; the shape is shown for reference. ```tsx type Maybe = T | undefined | null;