Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Helper constants, utilities, and configuration options for advanced grid customization and performance optimization.
Built-in constants for configuring grid behavior and handling grid events.
const _constants = {
/** Cell navigation modes */
CellNavigationMode: {
NONE: 'none';
CHANGE_ROW: 'changeRow';
LOOP_OVER_ROW: 'loopOverRow';
};
/** Grid update action types */
UpdateActions: {
CELL_UPDATE: 'CELL_UPDATE';
COLUMN_FILL: 'COLUMN_FILL';
COPY_PASTE: 'COPY_PASTE';
CELL_DRAG: 'CELL_DRAG';
};
/** Event type constants */
EventTypes: {
SELECT_CELL: 'SELECT_CELL';
SELECT_START: 'SELECT_START';
SELECT_UPDATE: 'SELECT_UPDATE';
SELECT_END: 'SELECT_END';
DRAG_ENTER: 'DRAG_ENTER';
SCROLL_TO_COLUMN: 'SCROLL_TO_COLUMN';
};
/** Header row type constants */
HeaderRowType: {
HEADER: 'header';
FILTER: 'filter';
};
/** Cell expansion icons */
CellExpand: {
DOWN_TRIANGLE: '▼';
RIGHT_TRIANGLE: '▶';
};
/** Drag and drop item types */
DragItemTypes: {
Column: 'column';
};
};Constants for configuring cell navigation behavior.
/**
* Cell navigation mode constants
* Controls how keyboard navigation behaves between cells
*/
const CellNavigationMode = {
/** No automatic navigation between cells */
NONE: 'none';
/** Tab key loops within the current row */
LOOP_OVER_ROW: 'loopOverRow';
/** Tab key moves to next/previous row when reaching row end */
CHANGE_ROW: 'changeRow';
};Usage Example:
import ReactDataGrid, { _constants } from 'react-data-grid';
const NavigationGrid = () => {
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
enableCellSelect={true}
cellNavigationMode={_constants.CellNavigationMode.CHANGE_ROW}
/>
);
};Constants identifying different types of grid data updates.
/**
* Update action type constants
* Used in onGridRowsUpdated events to identify update source
*/
const UpdateActions = {
/** Single cell edit via editor */
CELL_UPDATE: 'CELL_UPDATE';
/** Multiple cells updated via column fill/drag down */
COLUMN_FILL: 'COLUMN_FILL';
/** Cells updated via copy/paste operation */
COPY_PASTE: 'COPY_PASTE';
/** Cells updated via drag and drop */
CELL_DRAG: 'CELL_DRAG';
};Usage Example:
import ReactDataGrid, { _constants } from 'react-data-grid';
const UpdateHandlingGrid = () => {
const handleGridRowsUpdated = ({ fromRow, toRow, updated, action }) => {
console.log('Update action:', action);
switch (action) {
case _constants.UpdateActions.CELL_UPDATE:
console.log('Single cell updated');
break;
case _constants.UpdateActions.COLUMN_FILL:
console.log('Column fill operation');
break;
case _constants.UpdateActions.COPY_PASTE:
console.log('Copy/paste operation');
break;
case _constants.UpdateActions.CELL_DRAG:
console.log('Drag operation');
break;
}
// Handle the update
updateRows(fromRow, toRow, updated);
};
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
enableCellSelect={true}
onGridRowsUpdated={handleGridRowsUpdated}
/>
);
};Constants for internal grid event types.
/**
* Grid event type constants
* Used internally for grid event system
*/
const EventTypes = {
/** Cell selection event */
SELECT_CELL: 'SELECT_CELL';
/** Selection range start event */
SELECT_START: 'SELECT_START';
/** Selection range update event */
SELECT_UPDATE: 'SELECT_UPDATE';
/** Selection range end event */
SELECT_END: 'SELECT_END';
/** Drag enter event */
DRAG_ENTER: 'DRAG_ENTER';
/** Scroll to column event */
SCROLL_TO_COLUMN: 'SCROLL_TO_COLUMN';
};Performance optimization utility for determining when rows should re-render.
/**
* Row comparison utility for performance optimization
* Determines whether a row component should update based on prop changes
* @param nextProps - New props for the row
* @param currentProps - Current props for the row
* @returns boolean indicating whether the row should update
*/
const RowComparer: (nextProps: any, currentProps: any) => boolean;Usage Example:
import React, { memo } from 'react';
import ReactDataGrid, { RowComparer } from 'react-data-grid';
// Optimized row component using RowComparer
const OptimizedRow = memo((props) => {
return <Row {...props} />;
}, RowComparer);
const PerformanceOptimizedGrid = () => {
return (
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
rowComponent={OptimizedRow}
/>
);
};Test utilities and helper functions for grid development and testing.
const _helpers = {
/** Test utilities for grid components */
test: {
/** Helper utilities for creating grid props in tests */
GridPropHelpers: {
/** Create basic column definitions for testing */
createColumns: (count: number) => Column[];
/** Create basic row data for testing */
createRows: (count: number, columns: Column[]) => any[];
/** Create minimal grid props for testing */
createGridProps: (overrides?: Partial<ReactDataGridProps>) => ReactDataGridProps;
};
};
};Testing Example:
import { _helpers } from 'react-data-grid';
import { render } from '@testing-library/react';
const { GridPropHelpers } = _helpers.test;
describe('Grid Tests', () => {
test('renders basic grid', () => {
const columns = GridPropHelpers.createColumns(3);
const rows = GridPropHelpers.createRows(10, columns);
const props = GridPropHelpers.createGridProps({
columns,
rowGetter: i => rows[i],
rowsCount: rows.length
});
const { container } = render(<ReactDataGrid {...props} />);
expect(container).toBeInTheDocument();
});
});PropTypes shapes and validation helpers for column definitions.
const shapes = {
/** PropTypes shape for column definitions */
Column: PropTypes.shape({
key: PropTypes.string.isRequired,
name: PropTypes.node.isRequired,
width: PropTypes.number,
resizable: PropTypes.bool,
sortable: PropTypes.bool,
filterable: PropTypes.bool,
editable: PropTypes.bool,
formatter: PropTypes.node,
editor: PropTypes.node,
headerRenderer: PropTypes.node,
frozen: PropTypes.bool,
events: PropTypes.object
});
};Additional utility functions for common grid operations.
/**
* Utility functions for grid operations
*/
interface GridUtils {
/** Calculate column widths based on content */
calculateColumnWidths: (columns: Column[], rows: any[]) => Column[];
/** Sort rows by column */
sortRows: (rows: any[], sortColumn: string, sortDirection: SortDirection) => any[];
/** Filter rows based on filter criteria */
filterRows: (rows: any[], filters: Filter[]) => any[];
/** Export grid data to CSV */
exportToCSV: (columns: Column[], rows: any[], filename?: string) => void;
/** Import CSV data to grid format */
importFromCSV: (csvData: string, columns: Column[]) => any[];
}Utility Usage Examples:
import ReactDataGrid, { _constants } from 'react-data-grid';
// Advanced grid with multiple utilities
const AdvancedUtilityGrid = () => {
const [rows, setRows] = useState(initialRows);
const [sortColumn, setSortColumn] = useState('');
const [sortDirection, setSortDirection] = useState('NONE');
const [filters, setFilters] = useState([]);
// Custom sort implementation
const handleGridSort = (columnKey, direction) => {
setSortColumn(columnKey);
setSortDirection(direction);
const sortedRows = [...rows].sort((a, b) => {
const aValue = a[columnKey];
const bValue = b[columnKey];
if (direction === 'ASC') {
return aValue < bValue ? -1 : aValue > bValue ? 1 : 0;
} else {
return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
}
});
setRows(sortedRows);
};
// Export functionality
const exportData = () => {
const csv = convertToCSV(columns, rows);
downloadCSV(csv, 'grid-data.csv');
};
// Filter functionality
const handleFilter = (filter) => {
const newFilters = [...filters, filter];
setFilters(newFilters);
const filteredRows = initialRows.filter(row => {
return newFilters.every(f => {
const cellValue = String(row[f.columnKey]).toLowerCase();
const filterTerm = f.filterTerm.toLowerCase();
return cellValue.includes(filterTerm);
});
});
setRows(filteredRows);
};
return (
<div>
<div style={{ marginBottom: '10px' }}>
<button onClick={exportData}>Export to CSV</button>
<span style={{ marginLeft: '20px' }}>
Showing {rows.length} of {initialRows.length} rows
</span>
</div>
<ReactDataGrid
columns={columns}
rowGetter={i => rows[i]}
rowsCount={rows.length}
minHeight={400}
enableCellSelect={true}
cellNavigationMode={_constants.CellNavigationMode.CHANGE_ROW}
sortColumn={sortColumn}
sortDirection={sortDirection}
onGridSort={handleGridSort}
onFilter={handleFilter}
onGridRowsUpdated={({ action, ...args }) => {
console.log('Update action:', action);
if (action === _constants.UpdateActions.COPY_PASTE) {
console.log('Paste operation detected');
}
handleRowUpdate(args);
}}
/>
</div>
);
};
// Helper functions
const convertToCSV = (columns, rows) => {
const headers = columns.map(col => col.name).join(',');
const csvRows = rows.map(row =>
columns.map(col => JSON.stringify(row[col.key] || '')).join(',')
);
return [headers, ...csvRows].join('\n');
};
const downloadCSV = (csv, filename) => {
const blob = new Blob([csv], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
};Utilities and patterns for optimizing grid performance with large datasets.
// Virtual scrolling configuration
const largeDatasetGrid = {
// Overscan configuration for virtual scrolling
overScan: {
colsStart: 2, // Columns to render before viewport
colsEnd: 2, // Columns to render after viewport
rowsStart: 5, // Rows to render before viewport
rowsEnd: 5 // Rows to render after viewport
}
};
// Memory-efficient row getter
const memoizedRowGetter = useMemo(() => {
const cache = new Map();
return (index) => {
if (!cache.has(index)) {
cache.set(index, processRowData(rawData[index]));
}
return cache.get(index);
};
}, [rawData]);
// Optimized column definitions
const optimizedColumns = useMemo(() =>
columns.map(col => ({
...col,
formatter: col.formatter ? memo(col.formatter) : undefined
})),
[columns]
);Common configuration presets for different use cases.
// Preset configurations
const GridPresets = {
// Basic read-only grid
ReadOnly: {
enableCellSelect: false,
cellNavigationMode: _constants.CellNavigationMode.NONE
},
// Excel-like editing grid
ExcelLike: {
enableCellSelect: true,
cellNavigationMode: _constants.CellNavigationMode.CHANGE_ROW,
enableCellAutoFocus: true
},
// Selection-focused grid
Selection: {
enableCellSelect: true,
rowSelection: {
showCheckbox: true,
enableShiftSelect: true
}
},
// Performance-optimized for large datasets
HighPerformance: {
enableCellAutoFocus: false,
overScan: { colsStart: 1, colsEnd: 1, rowsStart: 2, rowsEnd: 2 }
}
};Install with Tessl CLI
npx tessl i tessl/npm-react-data-grid@6.1.1