diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6dcfc94696..9e44b02932 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,21 +8,12 @@ on: jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - react: [18, 19] - fail-fast: false steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 23 check-latest: true - - name: set up react 19 - if: matrix.react == 19 - run: | - node ./.github/workflows/patch-react19.js - cat package.json - name: npm install run: npm i - name: Biome @@ -45,12 +36,11 @@ jobs: run: node --run test timeout-minutes: 4 - name: Upload coverage - if: matrix.react == 18 uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - name: Deploy gh-pages - if: matrix.react == 18 && github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: | git config --global user.email 'action@github.com' git config --global user.name 'GitHub Action' diff --git a/.github/workflows/patch-react19.js b/.github/workflows/patch-react19.js deleted file mode 100644 index ea01f4dfa7..0000000000 --- a/.github/workflows/patch-react19.js +++ /dev/null @@ -1,15 +0,0 @@ -import fs from 'node:fs/promises'; - -const pkgText = await fs.readFile('./package.json', 'utf8'); -const pkg = JSON.parse(pkgText); - -pkg.devDependencies['@types/react'] = '^19.0.0'; -pkg.devDependencies['@types/react-dom'] = '^19.0.0'; -pkg.devDependencies.react = '^19.0.0'; -pkg.devDependencies['react-dom'] = '^19.0.0'; -pkg.overrides = { - '@types/react': '^19.0.0', - '@types/react-dom': '^19.0.0' -}; - -fs.writeFile('./package.json', JSON.stringify(pkg, null, 2)); diff --git a/README.md b/README.md index ef726156e7..b3174d4163 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ ## Features -- [React 18.0+](package.json) support +- [React 19.0+](package.json) support - [Evergreen browsers and server-side rendering](browserslist) support - Tree-shaking support and only [one npm dependency](package.json) to keep your bundles slim - Great performance thanks to virtualization: columns and rows outside the viewport are not rendered @@ -42,7 +42,7 @@ - [Cell copy / pasting](https://adazzle.github.io/react-data-grid/#/AllFeatures) - [Cell value dragging / filling](https://adazzle.github.io/react-data-grid/#/AllFeatures) - [Customizable Renderers](https://adazzle.github.io/react-data-grid/#/CustomizableRenderers) -- Right-to-left (RTL) support. We recommend using Firefox as Chrome has a [bug](https://bugs.chromium.org/p/chromium/issues/detail?id=1140374) with frozen columns. +- Right-to-left (RTL) support. We recommend using Firefox as Chrome has a [bug](https://issues.chromium.org/issues/40653832) with frozen columns. ## Links @@ -419,16 +419,16 @@ interface Renderers { } ``` -For example, the default `` component can be wrapped via the `renderRow` prop to add context providers or tweak props +For example, 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'; function myRowRenderer(key: React.Key, props: RenderRowProps) { return ( - + - + ); } diff --git a/eslint.config.js b/eslint.config.js index 3385e644f0..2f1771e43c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -564,11 +564,6 @@ export default [ importNames: ['default'], message: 'Use named imports instead.' }, - { - name: 'react', - importNames: ['useLayoutEffect'], - message: 'Use the override from src/hooks instead.' - }, { name: 'react-dom', importNames: ['default'], diff --git a/package.json b/package.json index 31ab6360a6..666f94b96e 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "@testing-library/react": "^16.2.0", "@testing-library/user-event": "^14.5.2", "@types/node": "^22.12.0", - "@types/react": "^18.3.9", - "@types/react-dom": "^18.3.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", "@typescript-eslint/eslint-plugin": "^8.22.0", "@typescript-eslint/parser": "^8.22.0", "@vitejs/plugin-react": "^4.3.4", @@ -93,10 +93,10 @@ "playwright": "^1.50.0", "postcss": "^8.5.1", "prettier": "3.5.0", - "react": "^18.3.1", + "react": "^19.0.0", "react-dnd": "^16.0.1", "react-dnd-html5-backend": "^16.0.1", - "react-dom": "^18.3.1", + "react-dom": "^19.0.0", "rolldown": "^1.0.0-beta.3", "typescript": "~5.7.3", "vite": "^6.0.11", diff --git a/src/Cell.tsx b/src/Cell.tsx index 29cd201693..f41b01d1d1 100644 --- a/src/Cell.tsx +++ b/src/Cell.tsx @@ -1,4 +1,4 @@ -import { forwardRef, memo, type RefAttributes } from 'react'; +import { memo } from 'react'; import { css } from '@linaria/core'; import { useRovingTabIndex } from './hooks'; @@ -25,26 +25,23 @@ const cellDraggedOver = css` const cellDraggedOverClassname = `rdg-cell-dragged-over ${cellDraggedOver}`; -function Cell( - { - column, - colSpan, - isCellSelected, - isCopied, - isDraggedOver, - row, - rowIdx, - className, - onClick, - onDoubleClick, - onContextMenu, - onRowChange, - selectCell, - style, - ...props - }: CellRendererProps, - ref: React.Ref -) { +function Cell({ + column, + colSpan, + isCellSelected, + isCopied, + isDraggedOver, + row, + rowIdx, + className, + onClick, + onDoubleClick, + onContextMenu, + onRowChange, + selectCell, + style, + ...props +}: CellRendererProps) { const { tabIndex, childTabIndex, onFocus } = useRovingTabIndex(isCellSelected); const { cellClass } = column; @@ -101,7 +98,6 @@ function Cell( aria-colspan={colSpan} aria-selected={isCellSelected} aria-readonly={!isEditable || undefined} - ref={ref} tabIndex={tabIndex} className={className} style={{ @@ -126,9 +122,7 @@ function Cell( ); } -const CellComponent = memo(forwardRef(Cell)) as ( - props: CellRendererProps & RefAttributes -) => React.JSX.Element; +const CellComponent = memo(Cell) as (props: CellRendererProps) => React.JSX.Element; export default CellComponent; diff --git a/src/DataGrid.tsx b/src/DataGrid.tsx index ebe1357ad7..81689fa6e4 100644 --- a/src/DataGrid.tsx +++ b/src/DataGrid.tsx @@ -1,17 +1,23 @@ -import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; -import type { Key, KeyboardEvent, RefAttributes } from 'react'; +import { + useCallback, + useImperativeHandle, + useLayoutEffect, + useMemo, + useRef, + useState +} from 'react'; +import type { Key, KeyboardEvent } from 'react'; import { flushSync } from 'react-dom'; import clsx from 'clsx'; import { - HeaderRowSelectionChangeProvider, - HeaderRowSelectionProvider, - RowSelectionChangeProvider, + HeaderRowSelectionChangeContext, + HeaderRowSelectionContext, + RowSelectionChangeContext, useCalculatedColumns, useColumnWidths, useGridDimensions, useLatestFunc, - useLayoutEffect, useViewportColumns, useViewportRows, type HeaderRowSelectionContextValue @@ -55,9 +61,9 @@ import type { import { defaultRenderCell } from './Cell'; import { renderCheckbox as defaultRenderCheckbox } from './cellRenderers'; import { - DataGridDefaultRenderersProvider, + DataGridDefaultRenderersContext, useDefaultRenderers -} from './DataGridDefaultRenderersProvider'; +} from './DataGridDefaultRenderersContext'; import DragHandle from './DragHandle'; import EditCell from './EditCell'; import GroupedColumnHeaderRow from './GroupedColumnHeaderRow'; @@ -97,7 +103,7 @@ export interface DataGridHandle { } type SharedDivProps = Pick< - React.HTMLAttributes, + React.ComponentProps<'div'>, | 'role' | 'aria-label' | 'aria-labelledby' @@ -109,6 +115,7 @@ type SharedDivProps = Pick< >; export interface DataGridProps extends SharedDivProps { + ref?: Maybe>; /** * Grid and data Props */ @@ -215,11 +222,11 @@ export interface DataGridProps extends Sha * * */ -function DataGrid( - props: DataGridProps, - ref: React.Ref +export default function DataGrid( + props: DataGridProps ) { const { + ref, // Grid and data Props columns: rawColumns, rows, @@ -1125,9 +1132,9 @@ function DataGrid( data-testid={testId} data-cy={dataCy} > - - - + + + {Array.from({ length: groupedColumnHeaderRowsCount }, (_, index) => ( ( shouldFocusGrid={!selectedCellIsWithinSelectionBounds} direction={direction} /> - - + + {rows.length === 0 && noRowsFallback ? ( noRowsFallback ) : ( @@ -1184,9 +1191,9 @@ function DataGrid( /> ); })} - + {getViewportRows()} - + {bottomSummaryRows?.map((row, rowIdx) => { const gridRowStart = headerAndTopSummaryRowsCount + rows.length + rowIdx + 1; const summaryRowIdx = rows.length + rowIdx; @@ -1219,7 +1226,7 @@ function DataGrid( })} )} - + {renderDragHandle()} @@ -1262,7 +1269,3 @@ function getCellToScroll(gridEl: HTMLDivElement) { function isSamePosition(p1: Position, p2: Position) { return p1.idx === p2.idx && p1.rowIdx === p2.rowIdx; } - -export default forwardRef(DataGrid) as ( - props: DataGridProps & RefAttributes -) => React.JSX.Element; diff --git a/src/DataGridDefaultRenderersProvider.ts b/src/DataGridDefaultRenderersContext.ts similarity index 61% rename from src/DataGridDefaultRenderersProvider.ts rename to src/DataGridDefaultRenderersContext.ts index 79d0df609e..5cf1bcbe10 100644 --- a/src/DataGridDefaultRenderersProvider.ts +++ b/src/DataGridDefaultRenderersContext.ts @@ -3,9 +3,7 @@ import { createContext, useContext } from 'react'; import type { Maybe, Renderers } from './types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any -const DataGridDefaultRenderersContext = createContext>>(undefined); - -export const DataGridDefaultRenderersProvider = DataGridDefaultRenderersContext.Provider; +export const DataGridDefaultRenderersContext = createContext>>(undefined); export function useDefaultRenderers(): Maybe> { return useContext(DataGridDefaultRenderersContext); diff --git a/src/DragHandle.tsx b/src/DragHandle.tsx index f398260535..78cbe6ba94 100644 --- a/src/DragHandle.tsx +++ b/src/DragHandle.tsx @@ -32,11 +32,6 @@ const cellDragHandleFrozenClassname = css` const cellDragHandleClassname = `rdg-cell-drag-handle ${cellDragHandle}`; -// TODO: replace with RefObject once we drop support for React 18 -interface LatestDraggedOverRowIdxRef { - readonly current: number | undefined; -} - interface Props extends Pick, 'rows' | 'onRowsChange'> { gridRowStart: number; column: CalculatedColumn; @@ -44,7 +39,7 @@ interface Props extends Pick, 'rows' | 'onRowsChange maxColIdx: number; isLastRow: boolean; selectedPosition: SelectCellState; - latestDraggedOverRowIdx: LatestDraggedOverRowIdxRef; + latestDraggedOverRowIdx: React.RefObject; isCellEditable: (position: Position) => boolean; onClick: () => void; onFill: (event: FillEvent) => R; diff --git a/src/EditCell.tsx b/src/EditCell.tsx index 436f591ea3..2ede7736de 100644 --- a/src/EditCell.tsx +++ b/src/EditCell.tsx @@ -56,7 +56,7 @@ export default function EditCell({ onKeyDown, navigate }: EditCellProps) { - const frameRequestRef = useRef(undefined); + const frameRequestRef = useRef(undefined); const commitOnOutsideClick = column.editorOptions?.commitOnOutsideClick !== false; // We need to prevent the `useEffect` from cleaning up between re-renders, diff --git a/src/GroupRow.tsx b/src/GroupRow.tsx index 57c9f58adf..9fb0fd7443 100644 --- a/src/GroupRow.tsx +++ b/src/GroupRow.tsx @@ -2,7 +2,7 @@ import { memo, useMemo } from 'react'; import { css } from '@linaria/core'; import clsx from 'clsx'; -import { RowSelectionProvider, type RowSelectionContextValue } from './hooks'; +import { RowSelectionContext, type RowSelectionContextValue } from './hooks'; import { getRowStyle } from './utils'; import type { BaseRenderRowProps, GroupRow } from './types'; import { SELECT_COLUMN_KEY } from './Columns'; @@ -58,7 +58,7 @@ function GroupedRow({ ); return ( - +
({ /> ))}
-
+ ); } diff --git a/src/HeaderCell.tsx b/src/HeaderCell.tsx index 6fbf3c770a..ea84e2ebff 100644 --- a/src/HeaderCell.tsx +++ b/src/HeaderCell.tsx @@ -260,7 +260,7 @@ export default function HeaderCell({ } } - let draggableProps: React.HTMLAttributes | undefined; + let draggableProps: React.ComponentProps<'div'> | undefined; if (draggable) { draggableProps = { draggable: true, diff --git a/src/Row.tsx b/src/Row.tsx index 9b811a3397..11628b6251 100644 --- a/src/Row.tsx +++ b/src/Row.tsx @@ -1,38 +1,35 @@ -import { forwardRef, memo, useMemo, type RefAttributes } from 'react'; +import { memo, useMemo } from 'react'; import clsx from 'clsx'; -import { RowSelectionProvider, useLatestFunc, type RowSelectionContextValue } from './hooks'; +import { RowSelectionContext, useLatestFunc, type RowSelectionContextValue } from './hooks'; import { getColSpan, getRowStyle } from './utils'; import type { CalculatedColumn, RenderRowProps } from './types'; -import { useDefaultRenderers } from './DataGridDefaultRenderersProvider'; +import { useDefaultRenderers } from './DataGridDefaultRenderersContext'; import { rowClassname, rowSelectedClassname } from './style/row'; -function Row( - { - className, - rowIdx, - gridRowStart, - selectedCellIdx, - isRowSelectionDisabled, - isRowSelected, - copiedCellIdx, - draggedOverCellIdx, - lastFrozenColumnIndex, - row, - viewportColumns, - selectedCellEditor, - onCellClick, - onCellDoubleClick, - onCellContextMenu, - rowClass, - setDraggedOverRowIdx, - onMouseEnter, - onRowChange, - selectCell, - ...props - }: RenderRowProps, - ref: React.Ref -) { +function Row({ + className, + rowIdx, + gridRowStart, + selectedCellIdx, + isRowSelectionDisabled, + isRowSelected, + copiedCellIdx, + draggedOverCellIdx, + lastFrozenColumnIndex, + row, + viewportColumns, + selectedCellEditor, + onCellClick, + onCellDoubleClick, + onCellContextMenu, + rowClass, + setDraggedOverRowIdx, + onMouseEnter, + onRowChange, + selectCell, + ...props +}: RenderRowProps) { const renderCell = useDefaultRenderers()!.renderCell!; const handleRowChange = useLatestFunc((column: CalculatedColumn, newRow: R) => { @@ -94,10 +91,9 @@ function Row( ); return ( - +
( > {cells}
-
+ ); } -const RowComponent = memo(forwardRef(Row)) as ( - props: RenderRowProps & RefAttributes -) => React.JSX.Element; +const RowComponent = memo(Row) as (props: RenderRowProps) => React.JSX.Element; export default RowComponent; diff --git a/src/ScrollToCell.tsx b/src/ScrollToCell.tsx index 5c8a228eec..c1b32fff34 100644 --- a/src/ScrollToCell.tsx +++ b/src/ScrollToCell.tsx @@ -1,6 +1,5 @@ -import { useRef } from 'react'; +import { useLayoutEffect, useRef } from 'react'; -import { useLayoutEffect } from './hooks'; import { scrollIntoView } from './utils'; export interface PartialPosition { diff --git a/src/TreeDataGrid.tsx b/src/TreeDataGrid.tsx index 238172f084..fc714c4a57 100644 --- a/src/TreeDataGrid.tsx +++ b/src/TreeDataGrid.tsx @@ -1,5 +1,5 @@ -import { forwardRef, useCallback, useMemo } from 'react'; -import type { Key, RefAttributes } from 'react'; +import { useCallback, useMemo } from 'react'; +import type { Key } from 'react'; import { useLatestFunc } from './hooks'; import { assertIsValidKeyGetter, isCtrlKeyHeldDown } from './utils'; @@ -17,8 +17,8 @@ import type { import { renderToggleGroup } from './cellRenderers'; import { SELECT_COLUMN_KEY } from './Columns'; import DataGrid from './DataGrid'; -import type { DataGridHandle, DataGridProps } from './DataGrid'; -import { useDefaultRenderers } from './DataGridDefaultRenderersProvider'; +import type { DataGridProps } from './DataGrid'; +import { useDefaultRenderers } from './DataGridDefaultRenderersContext'; import GroupedRow from './GroupRow'; import { defaultRenderRow } from './Row'; @@ -47,25 +47,22 @@ type GroupByDictionary = Record< } >; -function TreeDataGrid( - { - columns: rawColumns, - rows: rawRows, - rowHeight: rawRowHeight, - rowKeyGetter: rawRowKeyGetter, - onCellKeyDown: rawOnCellKeyDown, - onRowsChange, - selectedRows: rawSelectedRows, - onSelectedRowsChange: rawOnSelectedRowsChange, - renderers, - groupBy: rawGroupBy, - rowGrouper, - expandedGroupIds, - onExpandedGroupIdsChange, - ...props - }: TreeDataGridProps, - ref: React.Ref -) { +export function TreeDataGrid({ + columns: rawColumns, + rows: rawRows, + rowHeight: rawRowHeight, + rowKeyGetter: rawRowKeyGetter, + onCellKeyDown: rawOnCellKeyDown, + onRowsChange, + selectedRows: rawSelectedRows, + onSelectedRowsChange: rawOnSelectedRowsChange, + renderers, + groupBy: rawGroupBy, + rowGrouper, + expandedGroupIds, + onExpandedGroupIdsChange, + ...props +}: TreeDataGridProps) { const defaultRenderers = useDefaultRenderers(); const rawRenderRow = renderers?.renderRow ?? defaultRenderers?.renderRow ?? defaultRenderRow; const headerAndTopSummaryRowsCount = 1 + (props.topSummaryRows?.length ?? 0); @@ -417,7 +414,6 @@ function TreeDataGrid( aria-rowcount={ rowsCount + 1 + (props.topSummaryRows?.length ?? 0) + (props.bottomSummaryRows?.length ?? 0) } - ref={ref} columns={columns} rows={rows as R[]} // TODO: check types rowHeight={rowHeight} @@ -437,7 +433,3 @@ function TreeDataGrid( function isReadonlyArray(arr: unknown): arr is readonly unknown[] { return Array.isArray(arr); } - -export default forwardRef(TreeDataGrid) as ( - props: TreeDataGridProps & RefAttributes -) => React.JSX.Element; diff --git a/src/cellRenderers/SelectCellFormatter.tsx b/src/cellRenderers/SelectCellFormatter.tsx index 60799db2f9..e006386fd7 100644 --- a/src/cellRenderers/SelectCellFormatter.tsx +++ b/src/cellRenderers/SelectCellFormatter.tsx @@ -1,5 +1,5 @@ import type { RenderCheckboxProps } from '../types'; -import { useDefaultRenderers } from '../DataGridDefaultRenderersProvider'; +import { useDefaultRenderers } from '../DataGridDefaultRenderersContext'; type SharedInputProps = Pick< RenderCheckboxProps, diff --git a/src/hooks/index.ts b/src/hooks/index.ts index c836ce472f..709f729be0 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -2,7 +2,6 @@ export * from './useCalculatedColumns'; export * from './useColumnWidths'; export * from './useGridDimensions'; export * from './useLatestFunc'; -export * from './useLayoutEffect'; export * from './useRovingTabIndex'; export * from './useRowSelection'; export * from './useViewportColumns'; diff --git a/src/hooks/useColumnWidths.ts b/src/hooks/useColumnWidths.ts index 7c4bce746c..2f70109a15 100644 --- a/src/hooks/useColumnWidths.ts +++ b/src/hooks/useColumnWidths.ts @@ -1,8 +1,7 @@ -import { useRef } from 'react'; +import { useLayoutEffect, useRef } from 'react'; import { flushSync } from 'react-dom'; import type { CalculatedColumn, StateSetter } from '../types'; -import { useLayoutEffect } from './useLayoutEffect'; import type { DataGridProps } from '../DataGrid'; export function useColumnWidths( diff --git a/src/hooks/useGridDimensions.ts b/src/hooks/useGridDimensions.ts index ee7df45b4b..ddd2ac67e3 100644 --- a/src/hooks/useGridDimensions.ts +++ b/src/hooks/useGridDimensions.ts @@ -1,8 +1,6 @@ -import { useRef, useState } from 'react'; +import { useLayoutEffect, useRef, useState } from 'react'; import { flushSync } from 'react-dom'; -import { useLayoutEffect } from './useLayoutEffect'; - export function useGridDimensions() { const gridRef = useRef(null); const [inlineSize, setInlineSize] = useState(1); diff --git a/src/hooks/useLayoutEffect.ts b/src/hooks/useLayoutEffect.ts deleted file mode 100644 index 8700637319..0000000000 --- a/src/hooks/useLayoutEffect.ts +++ /dev/null @@ -1,6 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { useEffect, useLayoutEffect as useOriginalLayoutEffect } from 'react'; - -// Silence silly warning -// https://reactjs.org/link/uselayouteffect-ssr -export const useLayoutEffect = typeof window === 'undefined' ? useEffect : useOriginalLayoutEffect; diff --git a/src/hooks/useRowSelection.ts b/src/hooks/useRowSelection.ts index 6f67747854..a70438d811 100644 --- a/src/hooks/useRowSelection.ts +++ b/src/hooks/useRowSelection.ts @@ -7,17 +7,13 @@ export interface RowSelectionContextValue { readonly isRowSelectionDisabled: boolean; } -const RowSelectionContext = createContext(undefined); +export const RowSelectionContext = createContext(undefined); -export const RowSelectionProvider = RowSelectionContext.Provider; - -const RowSelectionChangeContext = createContext< +export const RowSelectionChangeContext = createContext< // eslint-disable-next-line @typescript-eslint/no-explicit-any ((selectRowEvent: SelectRowEvent) => void) | undefined >(undefined); -export const RowSelectionChangeProvider = RowSelectionChangeContext.Provider; - export function useRowSelection() { const rowSelectionContext = useContext(RowSelectionContext); const rowSelectionChangeContext = useContext(RowSelectionChangeContext); @@ -38,18 +34,14 @@ export interface HeaderRowSelectionContextValue { readonly isIndeterminate: boolean; } -const HeaderRowSelectionContext = createContext( +export const HeaderRowSelectionContext = createContext( undefined ); -export const HeaderRowSelectionProvider = HeaderRowSelectionContext.Provider; - -const HeaderRowSelectionChangeContext = createContext< +export const HeaderRowSelectionChangeContext = createContext< ((selectRowEvent: SelectHeaderRowEvent) => void) | undefined >(undefined); -export const HeaderRowSelectionChangeProvider = HeaderRowSelectionChangeContext.Provider; - export function useHeaderRowSelection() { const headerRowSelectionContext = useContext(HeaderRowSelectionContext); const headerRowSelectionChangeContext = useContext(HeaderRowSelectionChangeContext); diff --git a/src/index.ts b/src/index.ts index f016f98853..dbb217568d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,8 +6,8 @@ export { type DataGridHandle, type DefaultColumnOptions } from './DataGrid'; -export { default as TreeDataGrid, type TreeDataGridProps } from './TreeDataGrid'; -export { DataGridDefaultRenderersProvider } from './DataGridDefaultRenderersProvider'; +export { TreeDataGrid, type TreeDataGridProps } from './TreeDataGrid'; +export { DataGridDefaultRenderersContext } from './DataGridDefaultRenderersContext'; export { default as Row } from './Row'; export { default as Cell } from './Cell'; export * from './Columns'; diff --git a/src/renderHeaderCell.tsx b/src/renderHeaderCell.tsx index 1338a932d3..d7a848359d 100644 --- a/src/renderHeaderCell.tsx +++ b/src/renderHeaderCell.tsx @@ -1,7 +1,7 @@ import { css } from '@linaria/core'; import type { RenderHeaderCellProps } from './types'; -import { useDefaultRenderers } from './DataGridDefaultRenderersProvider'; +import { useDefaultRenderers } from './DataGridDefaultRenderersContext'; const headerSortCellClassname = css` @layer rdg.SortableHeaderCell { diff --git a/src/style/cell.ts b/src/style/cell.ts index 02afec16a0..3b5b9ce19c 100644 --- a/src/style/cell.ts +++ b/src/style/cell.ts @@ -6,7 +6,7 @@ export const cell = css` * dynamically switching between different containment styles incurs a heavy relayout penalty * Chromium bug: at odd zoom levels or subpixel positioning, * layout/paint/style containment can make cell borders disappear - * https://bugs.chromium.org/p/chromium/issues/detail?id=1326946 + * https://issues.chromium.org/issues/40840864 */ position: relative; /* needed for absolute positioning to work */ padding-block: 0; diff --git a/src/types.ts b/src/types.ts index ebd01a9671..f08dfddb54 100644 --- a/src/types.ts +++ b/src/types.ts @@ -149,10 +149,7 @@ export interface RenderHeaderCellProps { export interface CellRendererProps extends Pick, 'row' | 'rowIdx' | 'selectCell'>, - Omit< - React.HTMLAttributes, - 'children' | 'onClick' | 'onDoubleClick' | 'onContextMenu' - > { + Omit, 'children' | 'onClick' | 'onDoubleClick' | 'onContextMenu'> { column: CalculatedColumn; colSpan: number | undefined; isCopied: boolean; @@ -208,7 +205,7 @@ export interface CellSelectArgs { } export interface BaseRenderRowProps - extends Omit, 'style' | 'children'>, + extends Omit, 'style' | 'children'>, Pick< DataGridProps, 'onCellClick' | 'onCellDoubleClick' | 'onCellContextMenu' @@ -308,7 +305,7 @@ export interface RenderSortStatusProps extends RenderSortIconProps, RenderSortPr export interface RenderCheckboxProps extends Pick< - React.InputHTMLAttributes, + React.ComponentProps<'input'>, 'aria-label' | 'aria-labelledby' | 'checked' | 'tabIndex' | 'disabled' > { indeterminate?: boolean | undefined; diff --git a/test/browser/renderers.test.tsx b/test/browser/renderers.test.tsx index 687cbe3d07..ba84d59b1a 100644 --- a/test/browser/renderers.test.tsx +++ b/test/browser/renderers.test.tsx @@ -3,7 +3,7 @@ import { page, userEvent } from '@vitest/browser/context'; import DataGrid, { Cell, - DataGridDefaultRenderersProvider, + DataGridDefaultRenderersContext, Row as DefaultRow, renderSortIcon, SelectColumn @@ -96,9 +96,9 @@ function TestGrid(props: DataGridProps) { return ; } -function setupProvider(props: DataGridProps) { +function setupContext(props: DataGridProps) { return page.render( - , renderCheckbox: renderGlobalCheckbox, @@ -108,7 +108,7 @@ function setupProvider(props: DataGridProps - + ); } @@ -119,15 +119,15 @@ test('fallback defined using renderers prop with no rows', async () => { await expect.element(page.getByText('Local no rows fallback')).toBeInTheDocument(); }); -test('fallback defined using provider with no rows', async () => { - setupProvider({ columns, rows: noRows }); +test('fallback defined using context with no rows', async () => { + setupContext({ columns, rows: noRows }); expect(getRowsOld()).toHaveLength(0); await expect.element(page.getByText('Global no rows fallback')).toBeInTheDocument(); }); -test('fallback defined using both provider and renderers with no rows', async () => { - setupProvider({ columns, rows: noRows, renderers: { noRowsFallback: } }); +test('fallback defined using both context and renderers with no rows', async () => { + setupContext({ columns, rows: noRows, renderers: { noRowsFallback: } }); expect(getRowsOld()).toHaveLength(0); await expect.element(page.getByText('Local no rows fallback')).toBeInTheDocument(); @@ -144,15 +144,15 @@ test('fallback defined using renderers prop with a row', async () => { await expect.element(page.getByText('Local no rows fallback')).not.toBeInTheDocument(); }); -test('fallback defined using provider with a row', async () => { - setupProvider({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }] }); +test('fallback defined using context with a row', async () => { + setupContext({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }] }); expect(getRowsOld()).toHaveLength(1); await expect.element(page.getByText('Global no rows fallback')).not.toBeInTheDocument(); }); -test('fallback defined using both provider and renderers with a row', async () => { - setupProvider({ +test('fallback defined using both context and renderers with a row', async () => { + setupContext({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }], renderers: { noRowsFallback: } @@ -170,23 +170,23 @@ test('checkbox defined using renderers prop', async () => { await expect.element(page.getByText('Local checkbox')).toBeInTheDocument(); }); -test('checkbox defined using provider', async () => { - setupProvider({ columns, rows: noRows }); +test('checkbox defined using context', async () => { + setupContext({ columns, rows: noRows }); expect(getRowsOld()).toHaveLength(0); await expect.element(page.getByText('Global checkbox')).toBeInTheDocument(); }); -test('checkbox defined using both provider and renderers', async () => { - setupProvider({ columns, rows: noRows, renderers: { renderCheckbox: renderLocalCheckbox } }); +test('checkbox defined using both context and renderers', async () => { + setupContext({ columns, rows: noRows, renderers: { renderCheckbox: renderLocalCheckbox } }); expect(getRowsOld()).toHaveLength(0); await expect.element(page.getByText('Local checkbox')).toBeInTheDocument(); await expect.element(page.getByText('Global checkbox')).not.toBeInTheDocument(); }); -test('sortPriority defined using both providers', async () => { - setupProvider({ columns, rows: noRows }); +test('sortPriority defined using both contexts', async () => { + setupContext({ columns, rows: noRows }); const [, headerCell2, headerCell3] = getHeaderCells(); await userEvent.click(headerCell2); @@ -200,8 +200,8 @@ test('sortPriority defined using both providers', async () => { await expect.element(page.getByTestId('local-sort-priority')).not.toBeInTheDocument(); }); -test('sortPriority defined using both providers and renderers', async () => { - setupProvider({ columns, rows: noRows, renderers: { renderSortStatus: renderLocalSortStatus } }); +test('sortPriority defined using both contexts and renderers', async () => { + setupContext({ columns, rows: noRows, renderers: { renderSortStatus: renderLocalSortStatus } }); const [, headerCell2, headerCell3] = getHeaderCells(); await userEvent.click(headerCell3); @@ -215,8 +215,8 @@ test('sortPriority defined using both providers and renderers', async () => { await expect.element(page.getByTestId('global-sort-priority')).not.toBeInTheDocument(); }); -test('renderCell defined using provider', async () => { - setupProvider({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }] }); +test('renderCell defined using context', async () => { + setupContext({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }] }); const [, cell1, cell2] = getCells(); await expect.element(cell1).toHaveTextContent('value 1'); @@ -230,8 +230,8 @@ test('renderCell defined using provider', async () => { await expect.element(cell2).toHaveStyle({ fontStyle: 'italic' }); }); -test('renderCell defined using both providers and renderers', async () => { - setupProvider({ +test('renderCell defined using both contexts and renderers', async () => { + setupContext({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }], renderers: { renderCell: renderLocalCell } @@ -249,16 +249,16 @@ test('renderCell defined using both providers and renderers', async () => { await expect.element(cell2).toHaveStyle({ fontStyle: 'normal' }); }); -test('renderRow defined using provider', () => { - setupProvider({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }] }); +test('renderRow defined using context', () => { + setupContext({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }] }); const [row] = getRowsOld(); expect(row).toHaveClass('global'); expect(row).not.toHaveClass('local'); }); -test('renderRow defined using both providers and renderers', () => { - setupProvider({ +test('renderRow defined using both contexts and renderers', () => { + setupContext({ columns, rows: [{ id: 1, col1: 'value 1', col2: 'value 2' }], renderers: { renderRow: renderLocalRow } diff --git a/tsconfig.base.json b/tsconfig.base.json index f445adcd03..8fd738efa5 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -4,6 +4,7 @@ "composite": true, "emitDeclarationOnly": true, "exactOptionalPropertyTypes": true, + "forceConsistentCasingInFileNames": true, "isolatedModules": true, "jsx": "react-jsx", "lib": ["ESNext"], diff --git a/tsconfig.vite.json b/tsconfig.vite.json index 5d69221237..f965998fcd 100644 --- a/tsconfig.vite.json +++ b/tsconfig.vite.json @@ -4,5 +4,5 @@ "skipLibCheck": true, "types": ["@vitest/browser/providers/playwright"] }, - "include": ["vite.config.ts", "vitest.workspace.ts"] + "include": ["vite.config.ts"] } diff --git a/website/directionContext.ts b/website/directionContext.ts index 5098898dc8..03d01540ad 100644 --- a/website/directionContext.ts +++ b/website/directionContext.ts @@ -2,9 +2,7 @@ import { createContext, useContext } from 'react'; import type { Direction } from '../src/types'; -const DirectionContext = createContext('ltr'); - -export const DirectionContextProvider = DirectionContext.Provider; +export const DirectionContext = createContext('ltr'); export function useDirection(): Direction { return useContext(DirectionContext); diff --git a/website/routes/ContextMenu.lazy.tsx b/website/routes/ContextMenu.lazy.tsx index 2b36620956..40ebe50d3d 100644 --- a/website/routes/ContextMenu.lazy.tsx +++ b/website/routes/ContextMenu.lazy.tsx @@ -1,4 +1,3 @@ -// eslint-disable-next-line @typescript-eslint/no-restricted-imports import { useLayoutEffect, useReducer, useRef, useState } from 'react'; import { createPortal } from 'react-dom'; import { faker } from '@faker-js/faker'; @@ -64,7 +63,7 @@ function ContextMenuDemo() { top: number; left: number; } | null>(null); - const menuRef = useRef(null); + const menuRef = useRef(null); const isContextMenuOpen = contextMenuProps !== null; useLayoutEffect(() => { diff --git a/website/routes/HeaderFilters.lazy.tsx b/website/routes/HeaderFilters.lazy.tsx index 68db339b37..44d758542a 100644 --- a/website/routes/HeaderFilters.lazy.tsx +++ b/website/routes/HeaderFilters.lazy.tsx @@ -287,7 +287,7 @@ function HeaderFilters() { Clear Filters - + - + {developerOptions.map(({ label, value }) => (