Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ interface Renderers<TRow, TSummaryRow> {
renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>;
renderRow?: Maybe<(key: Key, props: RenderRowProps<TRow, TSummaryRow>) => ReactNode>;
renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>;
renderCell?: Maybe<(key: Key, props: CellRendererProps<TRow, TSummaryRow>) => ReactNode>;
noRowsFallback?: Maybe<ReactNode>;
}
```
Expand Down
52 changes: 33 additions & 19 deletions src/Cell.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo } from 'react';
import { forwardRef, memo, type RefAttributes } from 'react';
import { css } from '@linaria/core';

import { useRovingTabIndex } from './hooks';
Expand All @@ -25,31 +25,36 @@ const cellDraggedOver = css`

const cellDraggedOverClassname = `rdg-cell-dragged-over ${cellDraggedOver}`;

function Cell<R, SR>({
column,
colSpan,
isCellSelected,
isCopied,
isDraggedOver,
row,
rowIdx,
onClick,
onDoubleClick,
onContextMenu,
onRowChange,
selectCell,
...props
}: CellRendererProps<R, SR>) {
function Cell<R, SR>(
{
column,
colSpan,
isCellSelected,
isCopied,
isDraggedOver,
row,
rowIdx,
className,
onClick,
onDoubleClick,
onContextMenu,
onRowChange,
selectCell,
...props
}: CellRendererProps<R, SR>,
ref: React.Ref<HTMLDivElement>
) {
const { tabIndex, childTabIndex, onFocus } = useRovingTabIndex(isCellSelected);

const { cellClass } = column;
const className = getCellClassname(
className = getCellClassname(
column,
{
[cellCopiedClassname]: isCopied,
[cellDraggedOverClassname]: isDraggedOver
},
typeof cellClass === 'function' ? cellClass(row) : cellClass
typeof cellClass === 'function' ? cellClass(row) : cellClass,
className
);
const isEditable = isCellEditable(column, row);

Expand Down Expand Up @@ -95,6 +100,7 @@ function Cell<R, SR>({
aria-colspan={colSpan}
aria-selected={isCellSelected}
aria-readonly={!isEditable || undefined}
ref={ref}
tabIndex={tabIndex}
className={className}
style={getCellStyle(column, colSpan)}
Expand All @@ -115,4 +121,12 @@ function Cell<R, SR>({
);
}

export default memo(Cell) as <R, SR>(props: CellRendererProps<R, SR>) => JSX.Element;
const CellComponent = memo(forwardRef(Cell)) as <R, SR>(
props: CellRendererProps<R, SR> & RefAttributes<HTMLDivElement>
) => JSX.Element;

export default CellComponent;

export function defaultRenderCell<R, SR>(key: React.Key, props: CellRendererProps<R, SR>) {
return <CellComponent key={key} {...props} />;
}
7 changes: 5 additions & 2 deletions src/DataGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import type {
SelectRowEvent,
SortColumn
} from './types';
import { defaultRenderCell } from './Cell';
import { renderCheckbox as defaultRenderCheckbox } from './cellRenderers';
import {
DataGridDefaultRenderersProvider,
Expand Down Expand Up @@ -252,6 +253,7 @@ function DataGrid<R, SR, K extends Key>(
const headerRowHeight = rawHeaderRowHeight ?? (typeof rowHeight === 'number' ? rowHeight : 35);
const summaryRowHeight = rawSummaryRowHeight ?? (typeof rowHeight === 'number' ? rowHeight : 35);
const renderRow = renderers?.renderRow ?? defaultRenderers?.renderRow ?? defaultRenderRow;
const renderCell = renderers?.renderCell ?? defaultRenderers?.renderCell ?? defaultRenderCell;
const renderSortStatus =
renderers?.renderSortStatus ?? defaultRenderers?.renderSortStatus ?? defaultRenderSortStatus;
const renderCheckbox =
Expand Down Expand Up @@ -334,9 +336,10 @@ function DataGrid<R, SR, K extends Key>(
const defaultGridComponents = useMemo(
() => ({
renderCheckbox,
renderSortStatus
renderSortStatus,
renderCell
}),
[renderCheckbox, renderSortStatus]
[renderCheckbox, renderSortStatus, renderCell]
);

const allRowsSelected = useMemo((): boolean => {
Expand Down
33 changes: 17 additions & 16 deletions src/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import clsx from 'clsx';
import { RowSelectionProvider, useLatestFunc } from './hooks';
import { getColSpan, getRowStyle } from './utils';
import type { CalculatedColumn, RenderRowProps } from './types';
import Cell from './Cell';
import { useDefaultRenderers } from './DataGridDefaultRenderersProvider';
import { rowClassname, rowSelectedClassname } from './style/row';

function Row<R, SR>(
Expand Down Expand Up @@ -33,6 +33,8 @@ function Row<R, SR>(
}: RenderRowProps<R, SR>,
ref: React.Ref<HTMLDivElement>
) {
const renderCell = useDefaultRenderers<R, SR>()!.renderCell!;

const handleRowChange = useLatestFunc((column: CalculatedColumn<R, SR>, newRow: R) => {
onRowChange(column, rowIdx, newRow);
});
Expand Down Expand Up @@ -68,21 +70,20 @@ function Row<R, SR>(
cells.push(selectedCellEditor);
} else {
cells.push(
<Cell
key={column.key}
column={column}
colSpan={colSpan}
row={row}
rowIdx={rowIdx}
isCopied={copiedCellIdx === idx}
isDraggedOver={draggedOverCellIdx === idx}
isCellSelected={isCellSelected}
onClick={onCellClick}
onDoubleClick={onCellDoubleClick}
onContextMenu={onCellContextMenu}
onRowChange={handleRowChange}
selectCell={selectCell}
/>
renderCell(column.key, {
column,
colSpan,
row,
rowIdx,
isCopied: copiedCellIdx === idx,
isDraggedOver: draggedOverCellIdx === idx,
isCellSelected,
onClick: onCellClick,
onDoubleClick: onCellDoubleClick,
onContextMenu: onCellContextMenu,
onRowChange: handleRowChange,
selectCell
})
);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { default, type DataGridProps, type DataGridHandle } from './DataGrid';
export { default as TreeDataGrid, type TreeDataGridProps } from './TreeDataGrid';
export { DataGridDefaultRenderersProvider } from './DataGridDefaultRenderersProvider';
export { default as Row } from './Row';
export { default as Cell } from './Cell';
export * from './Columns';
export * from './cellRenderers';
export { default as textEditor } from './editors/textEditor';
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ export interface Renderers<TRow, TSummaryRow> {
renderCheckbox?: Maybe<(props: RenderCheckboxProps) => ReactNode>;
renderRow?: Maybe<(key: Key, props: RenderRowProps<TRow, TSummaryRow>) => ReactNode>;
renderSortStatus?: Maybe<(props: RenderSortStatusProps) => ReactNode>;
renderCell?: Maybe<(key: Key, props: CellRendererProps<TRow, TSummaryRow>) => ReactNode>;
noRowsFallback?: Maybe<ReactNode>;
}

Expand Down
52 changes: 49 additions & 3 deletions test/renderers.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@ import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

import DataGrid, { DataGridDefaultRenderersProvider, renderSortIcon, SelectColumn } from '../src';
import type { Column, DataGridProps, RenderSortStatusProps, SortColumn } from '../src';
import { getHeaderCells, getRows, render, setup } from './utils';
import type {
CellRendererProps,
Column,
DataGridProps,
RenderSortStatusProps,
SortColumn
} from '../src';
import { getCells, getHeaderCells, getRows, render, setup } from './utils';

interface Row {
id: number;
col1?: string;
col2?: string;
}

const columns: readonly Column<Row>[] = [
Expand All @@ -24,6 +32,22 @@ const columns: readonly Column<Row>[] = [
}
];

function globalCellRenderer(key: React.Key, props: CellRendererProps<Row, unknown>) {
return (
<div key={key} role="gridcell">
{props.row[props.column.key as keyof Row]}
</div>
);
}

function localCellRenderer(key: React.Key) {
return (
<div key={key} role="gridcell">
local
</div>
);
}

function NoRowsFallback() {
return <div>Local no rows fallback</div>;
}
Expand Down Expand Up @@ -70,7 +94,8 @@ function setupProvider<R, SR, K extends React.Key>(props: DataGridProps<R, SR, K
value={{
noRowsFallback: <GlobalNoRowsFallback />,
renderCheckbox: globalRenderCheckbox,
renderSortStatus: globalSortStatus
renderSortStatus: globalSortStatus,
renderCell: globalCellRenderer
}}
>
<TestGrid {...props} />
Expand Down Expand Up @@ -174,3 +199,24 @@ test('sortPriority defined using both providers and renderers', async () => {

expect(screen.queryByTestId('global-sort-priority')).not.toBeInTheDocument();
});

test('renderCell defined using provider', () => {
setupProvider({ columns, rows: [{ id: 1, col1: 'col 1 value', col2: 'col 2 value' }] });

const [, cell1, cell2] = getCells();
expect(cell1).toHaveTextContent('col 1 value');
expect(cell2).toHaveTextContent('col 2 value');
});

test('renderCell defined using both providers ans renderers', () => {
setupProvider({
columns,
rows: [{ id: 1, col1: 'col 1 value', col2: 'col 2 value' }],
renderers: { renderCell: localCellRenderer }
});

const [selectCell, cell1, cell2] = getCells();
expect(selectCell).toHaveTextContent('local');
expect(cell1).toHaveTextContent('local');
expect(cell2).toHaveTextContent('local');
});