{!disableColumnMenu && isColumnNumeric && !column.disableColumnMenu && columnMenuIconButton}
diff --git a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx
index 94f8569d53739..1eaf265c449cf 100644
--- a/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx
+++ b/packages/grid/_modules_/grid/components/columnHeaders/GridColumnHeaders.tsx
@@ -5,7 +5,7 @@ import { useGridSelector } from '../../hooks/features/core/useGridSelector';
import { renderStateSelector } from '../../hooks/features/virtualization/renderingStateSelector';
import { optionsSelector } from '../../hooks/utils/optionsSelector';
import { GridApiContext } from '../GridApiContext';
-import { GridLeftEmptyCell, GridRightEmptyCell } from '../GridCell';
+import { GridEmptyCell } from '../GridEmptyCell';
import { GridScrollArea } from '../GridScrollArea';
import { gridContainerSizesSelector } from '../GridViewport';
import { GridColumnHeadersItemCollection } from './GridColumnHeadersItemCollection';
@@ -51,9 +51,9 @@ export const GridColumnsHeader = React.forwardRef(function G
style={{ minWidth: containerSizes?.totalSizes?.width }}
onDragOver={handleDragOver}
>
-
+
-
+
diff --git a/packages/grid/_modules_/grid/components/editCell/EditInputCell.tsx b/packages/grid/_modules_/grid/components/editCell/EditInputCell.tsx
index 7f6cbecf4bd12..16cb6306d2777 100644
--- a/packages/grid/_modules_/grid/components/editCell/EditInputCell.tsx
+++ b/packages/grid/_modules_/grid/components/editCell/EditInputCell.tsx
@@ -7,7 +7,9 @@ import { GridEditRowApi } from '../../models/api/gridEditRowApi';
export function EditInputCell(props: GridCellParams & InputBaseProps) {
const {
+ id,
value,
+ formattedValue,
api,
field,
row,
@@ -51,7 +53,7 @@ export function EditInputCell(props: GridCellParams & InputBaseProps) {
);
const inputType = mapColDefTypeToInputType(colDef.type);
- const formattedValue =
+ const inputFormattedValue =
valueState && isDate(valueState)
? formatDateToLocalInputDate({ value: valueState, withTime: colDef.type === 'dateTime' })
: valueState;
@@ -66,7 +68,7 @@ export function EditInputCell(props: GridCellParams & InputBaseProps) {
fullWidth
className="MuiDataGrid-editCellInputBase"
onKeyDown={onKeyDown}
- value={formattedValue}
+ value={inputFormattedValue}
onChange={onValueChange}
type={inputType}
{...inputBaseProps}
diff --git a/packages/grid/_modules_/grid/components/index.ts b/packages/grid/_modules_/grid/components/index.ts
index 35b31972b71a0..f25672967a6ca 100644
--- a/packages/grid/_modules_/grid/components/index.ts
+++ b/packages/grid/_modules_/grid/components/index.ts
@@ -22,3 +22,4 @@ export * from './GridStickyContainer';
export * from './GridViewport';
export * from './Watermark';
export * from './GridScrollArea';
+export * from './GridEmptyCell';
diff --git a/packages/grid/_modules_/grid/constants/eventsConstants.ts b/packages/grid/_modules_/grid/constants/eventsConstants.ts
index 4dab2c660ba8d..82f69f38ffa8d 100644
--- a/packages/grid/_modules_/grid/constants/eventsConstants.ts
+++ b/packages/grid/_modules_/grid/constants/eventsConstants.ts
@@ -1,8 +1,5 @@
// Web standard events
export const GRID_RESIZE = 'resize';
-export const GRID_CLICK = 'click';
-export const GRID_DOUBLE_CLICK = 'dblclick';
-export const GRID_MOUSE_HOVER = 'mouseover';
export const GRID_FOCUS_OUT = 'focusout';
export const GRID_KEYDOWN = 'keydown';
export const GRID_KEYUP = 'keyup';
@@ -10,31 +7,42 @@ export const GRID_SCROLL = 'scroll';
export const GRID_DRAGEND = 'dragend';
// GRID events
-export const GRID_CELL_CHANGE = 'cellChange';
-export const GRID_CELL_CHANGE_COMMITTED = 'cellChangeCommitted';
-export const GRID_CELL_MODE_CHANGE = 'cellModeChange';
-export const GRID_EDIT_ROW_MODEL_CHANGE = 'editRowModelChange';
export const GRID_COMPONENT_ERROR = 'componentError';
export const GRID_UNMOUNT = 'unmount';
export const GRID_ELEMENT_FOCUS_OUT = 'gridFocusOut';
+
+export const GRID_CELL_CHANGE = 'cellChange';
+export const GRID_CELL_CHANGE_COMMITTED = 'cellChangeCommitted';
+export const GRID_CELL_MODE_CHANGE = 'cellModeChange';
export const GRID_CELL_CLICK = 'cellClick';
-export const GRID_DOUBLE_CELL_CLICK = 'doubleCellClick';
-export const GRID_CELL_HOVER = 'cellHover';
+export const GRID_CELL_DOUBLE_CLICK = 'cellDoubleClick';
+export const GRID_CELL_OVER = 'cellOver';
+export const GRID_CELL_OUT = 'cellOut';
+export const GRID_CELL_ENTER = 'cellEnter';
+export const GRID_CELL_LEAVE = 'cellLeave';
+
export const GRID_ROW_CLICK = 'rowClick';
-export const GRID_DOUBLE_ROW_CLICK = 'doubleRowClick';
-export const GRID_ROW_HOVER = 'rowHover';
+export const GRID_ROW_DOUBLE_CLICK = 'rowDoubleClick';
+export const GRID_ROW_OVER = 'rowOver';
+export const GRID_ROW_OUT = 'rowOut';
+export const GRID_ROW_ENTER = 'rowEnter';
+export const GRID_ROW_LEAVE = 'rowLeave';
+
+export const GRID_COLUMN_HEADER_CLICK = 'columnHeaderClick';
+export const GRID_COLUMN_HEADER_DOUBLE_CLICK = 'columnHeaderDoubleClick';
+export const GRID_COLUMN_HEADER_OVER = 'columnHeaderOver';
+export const GRID_COLUMN_HEADER_OUT = 'columnHeaderOut';
+export const GRID_COLUMN_HEADER_ENTER = 'columnHeaderEnter';
+export const GRID_COLUMN_HEADER_LEAVE = 'columnHeaderLeave';
+
+export const GRID_EDIT_ROW_MODEL_CHANGE = 'editRowModelChange';
export const GRID_ROW_SELECTED = 'rowSelected';
export const GRID_SELECTION_CHANGED = 'selectionChange';
-export const GRID_COLUMN_HEADER_CLICK = 'columnClick';
-export const GRID_COLUMN_HEADER_HOVER = 'columnHeaderHover';
+
export const GRID_PAGE_CHANGED = 'pageChange';
export const GRID_PAGESIZE_CHANGED = 'pageSizeChange';
-export const GRID_COLUMN_FILTER_BUTTON_CLICK = 'colFilterButtonClick';
-export const GRID_COLUMN_MENU_BUTTON_CLICK = 'colMenuButtonClick';
-export const GRID_SCROLLING_START = 'scrolling:start';
export const GRID_SCROLLING = 'scrolling';
-export const GRID_SCROLLING_STOP = 'scrolling:stop';
export const GRID_COL_RESIZE_START = 'colResizing:start';
export const GRID_COL_RESIZE_STOP = 'colResizing:stop';
diff --git a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts
index 43de15831459e..48b9d1bc1b47b 100644
--- a/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts
+++ b/packages/grid/_modules_/grid/hooks/features/filter/useGridFilter.ts
@@ -11,7 +11,6 @@ import { GridFeatureModeConstant } from '../../../models/gridFeatureMode';
import { GridFilterItem, GridLinkOperator } from '../../../models/gridFilterItem';
import { GridFilterModelParams } from '../../../models/params/gridFilterModelParams';
import { GridRowsProp } from '../../../models/gridRows';
-import { buildGridCellParams } from '../../../utils/paramsUtils';
import { isDeepEqual } from '../../../utils/utils';
import { useGridApiEventHandler } from '../../root/useGridApiEventHandler';
import { useGridApiMethod } from '../../root/useGridApiMethod';
@@ -88,14 +87,8 @@ export const useGridFilter = (apiRef: GridApiRef, rowsProp: GridRowsProp): void
// This way we have latest rows on the first rendering
const rows = sortedGridRowsSelector(state);
- rows.forEach((row, rowIndex) => {
- const params = buildGridCellParams({
- rowModel: row,
- colDef: column,
- rowIndex,
- value: row[column.field],
- api: apiRef!.current!,
- });
+ rows.forEach((row) => {
+ const params = apiRef.current.getCellParams(row.id, filterItem.columnField!);
const isShown = applyFilterOnRow(params);
if (visibleRowsLookup[row.id] == null) {
diff --git a/packages/grid/_modules_/grid/hooks/features/rows/index.ts b/packages/grid/_modules_/grid/hooks/features/rows/index.ts
index e2f92894b076c..8eb76004cacb3 100644
--- a/packages/grid/_modules_/grid/hooks/features/rows/index.ts
+++ b/packages/grid/_modules_/grid/hooks/features/rows/index.ts
@@ -1,5 +1,6 @@
export * from './gridRowsSelector';
export * from './gridRowsState';
+export * from './useGridParamsApi';
export * from './useGridRows';
export * from './gridEditRowsSelector';
export * from './useGridEditRows';
diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts
index ebbc87ad26332..f97034ede3a57 100644
--- a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts
+++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts
@@ -17,7 +17,6 @@ import {
GridEditCellParams,
GridEditRowModelParams,
} from '../../../models/params/gridEditCellParams';
-import { buildGridCellParams } from '../../../utils/paramsUtils';
import { useGridApiEventHandler } from '../../root/useGridApiEventHandler';
import { useGridApiMethod } from '../../root/useGridApiMethod';
import { optionsSelector } from '../../utils/optionsSelector';
@@ -28,27 +27,6 @@ export function useGridEditRows(apiRef: GridApiRef) {
const [, setGridState, forceUpdate] = useGridState(apiRef);
const options = useGridSelector(apiRef, optionsSelector);
- const getCellValue = React.useCallback(
- (id: GridRowId, field: string) => {
- const colDef = apiRef.current.getColumnFromField(field);
- const rowModel = apiRef.current.getRowFromId(id);
-
- if (!colDef || !colDef.valueGetter) {
- return rowModel[field];
- }
-
- return colDef.valueGetter(
- buildGridCellParams({
- value: rowModel[field],
- colDef,
- rowModel,
- api: apiRef.current,
- }),
- );
- },
- [apiRef],
- );
-
const setCellEditMode = React.useCallback(
(id, field) => {
setGridState((state) => {
@@ -58,7 +36,7 @@ export function useGridEditRows(apiRef: GridApiRef) {
const currentCellEditState: GridEditRowsModel = { ...state.editRows };
currentCellEditState[id] = { ...currentCellEditState[id] } || {};
- currentCellEditState[id][field] = { value: getCellValue(id, field) };
+ currentCellEditState[id][field] = { value: apiRef.current.getCellValue(id, field) };
const newEditRowsState: GridEditRowsModel = { ...state.editRows, ...currentCellEditState };
@@ -78,7 +56,7 @@ export function useGridEditRows(apiRef: GridApiRef) {
};
apiRef.current.publishEvent(GRID_EDIT_ROW_MODEL_CHANGE, editRowParams);
},
- [apiRef, forceUpdate, getCellValue, setGridState],
+ [apiRef, forceUpdate, setGridState],
);
const setCellViewMode = React.useCallback(
@@ -219,7 +197,6 @@ export function useGridEditRows(apiRef: GridApiRef) {
useGridApiMethod
(
apiRef,
{
- getCellValue,
setCellMode,
onEditRowModelChange,
onCellModeChange,
diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts
new file mode 100644
index 0000000000000..4c533f6d3ff02
--- /dev/null
+++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridParamsApi.ts
@@ -0,0 +1,163 @@
+import * as React from 'react';
+import { GridApiRef } from '../../../models/api/gridApiRef';
+import { GridParamsApi } from '../../../models/api/gridParamsApi';
+import { GridRowId } from '../../../models/gridRows';
+import { GridCellParams, GridValueGetterParams } from '../../../models/params/gridCellParams';
+import { GridColParams } from '../../../models/params/gridColParams';
+import { GridRowParams } from '../../../models/params/gridRowParams';
+import {
+ getGridCellElement,
+ getGridColumnHeaderElement,
+ getGridRowElement,
+} from '../../../utils/domUtils';
+import { useGridApiMethod } from '../../root/useGridApiMethod';
+
+let warnedOnce = false;
+function warnMissingColumn(field) {
+ if (!warnedOnce && process.env.NODE_ENV !== 'production') {
+ console.warn(
+ [
+ `Material-UI: You are calling getValue('${field}') but the column \`${field}\` is not defined.`,
+ `Instead, you can access the data from \`params.row.${field}\`.`,
+ ].join('\n'),
+ );
+ warnedOnce = true;
+ }
+}
+export function useGridParamsApi(apiRef: GridApiRef) {
+ const getColumnHeaderParams = React.useCallback(
+ (field: string): GridColParams => ({
+ field,
+ element: apiRef.current.getColumnHeaderElement(field),
+ colDef: apiRef.current.getColumnFromField(field),
+ colIndex: apiRef.current.getColumnIndex(field, true),
+ api: apiRef!.current,
+ }),
+ [apiRef],
+ );
+
+ const getRowParams = React.useCallback(
+ (id: GridRowId) => {
+ const params: GridRowParams = {
+ id,
+ element: apiRef.current.getRowElement(id),
+ columns: apiRef.current.getAllColumns(),
+ getValue: (columnField: string) => apiRef.current.getCellValue(id, columnField),
+ row: apiRef.current.getRowFromId(id),
+ rowIndex: apiRef.current.getRowIndexFromId(id),
+ api: apiRef.current,
+ };
+ return params;
+ },
+ [apiRef],
+ );
+
+ const getBaseCellParams = React.useCallback(
+ (id: GridRowId, field: string) => {
+ const element = apiRef.current.getCellElement(id, field);
+ const row = apiRef.current.getRowFromId(id);
+
+ const params: GridValueGetterParams = {
+ element,
+ id,
+ field,
+ row,
+ value: row[field],
+ getValue: (columnField: string) => apiRef.current.getCellValue(id, columnField),
+ colDef: apiRef.current.getColumnFromField(field),
+ rowIndex: apiRef.current.getRowIndexFromId(id),
+ colIndex: apiRef.current.getColumnIndex(field, true),
+ api: apiRef.current,
+ };
+
+ return params;
+ },
+ [apiRef],
+ );
+
+ const getCellParams = React.useCallback(
+ (id: GridRowId, field: string) => {
+ const colDef = apiRef.current.getColumnFromField(field);
+ const element = apiRef.current.getCellElement(id, field);
+ const value = apiRef.current.getCellValue(id, field);
+ const baseParams = getBaseCellParams(id, field);
+ const params: GridCellParams = {
+ ...baseParams,
+ value,
+ getValue: (columnField: string) => apiRef.current.getCellValue(id, columnField),
+ formattedValue: value,
+ };
+ if (colDef.valueFormatter) {
+ params.formattedValue = colDef.valueFormatter(params);
+ }
+ const isEditableAttr = element && element.getAttribute('data-editable');
+ params.isEditable =
+ isEditableAttr != null
+ ? isEditableAttr === 'true'
+ : colDef && apiRef.current.isCellEditable(params);
+
+ return params;
+ },
+ [apiRef, getBaseCellParams],
+ );
+
+ const getCellValue = React.useCallback(
+ (id: GridRowId, field: string) => {
+ const colDef = apiRef.current.getColumnFromField(field);
+ const rowModel = apiRef.current.getRowFromId(id);
+
+ if (!colDef) {
+ warnMissingColumn(field);
+ }
+ if (!colDef || !colDef.valueGetter) {
+ return rowModel[field];
+ }
+
+ return colDef.valueGetter(getBaseCellParams(id, field));
+ },
+ [apiRef, getBaseCellParams],
+ );
+
+ const getColumnHeaderElement = React.useCallback(
+ (field: string): HTMLDivElement | null => {
+ if (!apiRef.current.rootElementRef!.current) {
+ return null;
+ }
+ return getGridColumnHeaderElement(apiRef.current.rootElementRef!.current!, field);
+ },
+ [apiRef],
+ );
+ const getRowElement = React.useCallback(
+ (id: GridRowId): HTMLDivElement | null => {
+ if (!apiRef.current.rootElementRef!.current) {
+ return null;
+ }
+ return getGridRowElement(apiRef.current.rootElementRef!.current!, id);
+ },
+ [apiRef],
+ );
+
+ const getCellElement = React.useCallback(
+ (id: GridRowId, field: string): HTMLDivElement | null => {
+ if (!apiRef.current.rootElementRef!.current) {
+ return null;
+ }
+ return getGridCellElement(apiRef.current.rootElementRef!.current!, { id, field });
+ },
+ [apiRef],
+ );
+
+ useGridApiMethod(
+ apiRef,
+ {
+ getCellValue,
+ getCellParams,
+ getCellElement,
+ getRowParams,
+ getRowElement,
+ getColumnHeaderParams,
+ getColumnHeaderElement,
+ },
+ 'CellApi',
+ );
+}
diff --git a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts
index 4304ab1e91369..5081728c899c7 100644
--- a/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts
+++ b/packages/grid/_modules_/grid/hooks/features/sorting/useGridSorting.ts
@@ -23,7 +23,6 @@ import {
GridSortModel,
GridSortDirection,
} from '../../../models/gridSortModel';
-import { buildGridCellParams } from '../../../utils/paramsUtils';
import { isDesc, nextGridSortDirection } from '../../../utils/sortingUtils';
import { isDeepEqual } from '../../../utils/utils';
import { useGridApiEventHandler } from '../../root/useGridApiEventHandler';
@@ -104,18 +103,8 @@ export const useGridSorting = (apiRef: GridApiRef, rowsProp: GridRowsProp) => {
comparator(
row1[field],
row2[field],
- buildGridCellParams({
- api: apiRef.current,
- colDef: apiRef.current.getColumnFromField(field),
- rowModel: row1,
- value: row1[field],
- }),
- buildGridCellParams({
- api: apiRef.current,
- colDef: apiRef.current.getColumnFromField(field),
- rowModel: row2,
- value: row2[field],
- }),
+ apiRef.current.getCellParams(row1.id, field),
+ apiRef.current.getCellParams(row2.id, field),
);
return res;
}, 0);
diff --git a/packages/grid/_modules_/grid/hooks/root/useEvents.ts b/packages/grid/_modules_/grid/hooks/root/useEvents.ts
index 2a10d3416583f..375c59b5322ea 100644
--- a/packages/grid/_modules_/grid/hooks/root/useEvents.ts
+++ b/packages/grid/_modules_/grid/hooks/root/useEvents.ts
@@ -1,14 +1,10 @@
import * as React from 'react';
import { GridApiRef } from '../../models/api/gridApiRef';
-import { GridCellParams } from '../../models/params/gridCellParams';
-import { GridColParams } from '../../models/params/gridColParams';
-import { GridRowParams } from '../../models/params/gridRowParams';
import { useGridSelector } from '../features/core/useGridSelector';
import { optionsSelector } from '../utils/optionsSelector';
import { useLogger } from '../utils/useLogger';
import {
GRID_CELL_CLICK,
- GRID_CLICK,
GRID_COL_RESIZE_START,
GRID_COL_RESIZE_STOP,
GRID_COLUMN_HEADER_CLICK,
@@ -17,23 +13,28 @@ import {
GRID_KEYUP,
GRID_RESIZE,
GRID_ROW_CLICK,
- GRID_MOUSE_HOVER,
- GRID_CELL_HOVER,
- GRID_ROW_HOVER,
- GRID_COLUMN_HEADER_HOVER,
+ GRID_CELL_OVER,
+ GRID_ROW_OVER,
GRID_FOCUS_OUT,
GRID_ELEMENT_FOCUS_OUT,
GRID_COMPONENT_ERROR,
GRID_STATE_CHANGE,
- GRID_DOUBLE_CELL_CLICK,
- GRID_DOUBLE_ROW_CLICK,
- GRID_DOUBLE_CLICK,
+ GRID_CELL_DOUBLE_CLICK,
+ GRID_ROW_DOUBLE_CLICK,
+ GRID_CELL_ENTER,
+ GRID_CELL_LEAVE,
+ GRID_CELL_OUT,
+ GRID_ROW_ENTER,
+ GRID_ROW_LEAVE,
+ GRID_ROW_OUT,
+ GRID_COLUMN_HEADER_LEAVE,
+ GRID_COLUMN_HEADER_ENTER,
+ GRID_COLUMN_HEADER_DOUBLE_CLICK,
+ GRID_COLUMN_HEADER_OVER,
+ GRID_COLUMN_HEADER_OUT,
} from '../../constants/eventsConstants';
-import { GRID_CELL_CSS_CLASS, GRID_ROW_CSS_CLASS } from '../../constants/cssClassesConstants';
-import { findParentElementFromClassName, getIdFromRowElem, isGridCell } from '../../utils/domUtils';
import { useGridApiMethod } from './useGridApiMethod';
import { useGridApiEventHandler } from './useGridApiEventHandler';
-import { buildGridCellParams, buildGridRowParams } from '../../utils/paramsUtils';
import { GridEventsApi } from '../../models/api/gridEventsApi';
export function useEvents(gridRootRef: React.RefObject, apiRef: GridApiRef): void {
@@ -47,114 +48,6 @@ export function useEvents(gridRootRef: React.RefObject, apiRef:
[apiRef],
);
- const getEventParams = React.useCallback(
- (event: MouseEvent) => {
- if (event.target == null) {
- throw new Error(
- `Event target null - Target has been removed or component might already be unmounted.`,
- );
- }
-
- const elem = event.target as HTMLElement;
- const eventParams: {
- cell?: GridCellParams;
- row?: GridRowParams;
- header?: GridColParams;
- } = {};
-
- if (isGridCell(elem)) {
- const cellEl = findParentElementFromClassName(elem, GRID_CELL_CSS_CLASS)! as HTMLElement;
- const rowEl = findParentElementFromClassName(elem, GRID_ROW_CSS_CLASS)! as HTMLElement;
- if (rowEl == null) {
- return null;
- }
- const id = getIdFromRowElem(rowEl);
- const rowModel = apiRef.current.getRowFromId(id);
- const rowIndex = apiRef.current.getRowIndexFromId(id);
- const field = cellEl.getAttribute('data-field') as string;
- const value = cellEl.getAttribute('data-value');
- const column = apiRef.current.getColumnFromField(field);
- if (!column || !column.disableClickEventBubbling) {
- const commonParams = {
- data: rowModel,
- rowIndex,
- colDef: column,
- rowModel,
- api: apiRef.current,
- };
- eventParams.cell = buildGridCellParams({
- ...commonParams,
- element: cellEl,
- value,
- });
- eventParams.row = buildGridRowParams({
- ...commonParams,
- element: rowEl,
- });
- }
- }
- return eventParams;
- },
- [apiRef],
- );
-
- const onClickHandler = React.useCallback(
- (event: MouseEvent) => {
- const eventParams = getEventParams(event);
-
- if (!eventParams) {
- return;
- }
-
- if (eventParams.cell) {
- apiRef.current.publishEvent(GRID_CELL_CLICK, eventParams.cell);
- }
- if (eventParams.row) {
- apiRef.current.publishEvent(GRID_ROW_CLICK, eventParams.row);
- }
- },
- [apiRef, getEventParams],
- );
-
- const onDoubleClickHandler = React.useCallback(
- (event: MouseEvent) => {
- const eventParams = getEventParams(event);
-
- if (!eventParams) {
- return;
- }
-
- if (eventParams.cell) {
- apiRef.current.publishEvent(GRID_DOUBLE_CELL_CLICK, eventParams.cell);
- }
- if (eventParams.row) {
- apiRef.current.publishEvent(GRID_DOUBLE_ROW_CLICK, eventParams.row);
- }
- },
- [apiRef, getEventParams],
- );
-
- const onHoverHandler = React.useCallback(
- (event: MouseEvent) => {
- const eventParams = getEventParams(event);
-
- if (!eventParams) {
- return;
- }
-
- if (eventParams.cell) {
- apiRef.current.publishEvent(GRID_CELL_HOVER, eventParams.cell);
- }
- if (eventParams.row) {
- apiRef.current.publishEvent(GRID_ROW_HOVER, eventParams.row);
- }
- if (eventParams.header) {
- apiRef.current.publishEvent(GRID_COLUMN_HEADER_HOVER, eventParams.header);
- }
- },
- [apiRef, getEventParams],
- );
-
const onFocusOutHandler = React.useCallback(
(event: FocusEvent) => {
apiRef.current.publishEvent(GRID_FOCUS_OUT, event);
@@ -194,13 +87,30 @@ export function useEvents(gridRootRef: React.RefObject, apiRef:
useGridApiEventHandler(apiRef, GRID_COL_RESIZE_STOP, handleResizeStop);
useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_CLICK, options.onColumnHeaderClick);
+ useGridApiEventHandler(
+ apiRef,
+ GRID_COLUMN_HEADER_DOUBLE_CLICK,
+ options.onColumnHeaderDoubleClick,
+ );
+ useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_OVER, options.onColumnHeaderOver);
+ useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_OUT, options.onColumnHeaderOut);
+ useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_ENTER, options.onColumnHeaderEnter);
+ useGridApiEventHandler(apiRef, GRID_COLUMN_HEADER_LEAVE, options.onColumnHeaderLeave);
+
useGridApiEventHandler(apiRef, GRID_CELL_CLICK, options.onCellClick);
+ useGridApiEventHandler(apiRef, GRID_CELL_DOUBLE_CLICK, options.onCellDoubleClick);
+ useGridApiEventHandler(apiRef, GRID_CELL_OVER, options.onCellOver);
+ useGridApiEventHandler(apiRef, GRID_CELL_OUT, options.onCellOut);
+ useGridApiEventHandler(apiRef, GRID_CELL_ENTER, options.onCellEnter);
+ useGridApiEventHandler(apiRef, GRID_CELL_LEAVE, options.onCellLeave);
+
+ useGridApiEventHandler(apiRef, GRID_ROW_DOUBLE_CLICK, options.onRowDoubleClick);
useGridApiEventHandler(apiRef, GRID_ROW_CLICK, options.onRowClick);
- useGridApiEventHandler(apiRef, GRID_DOUBLE_CELL_CLICK, options.onCellDoubleClick);
- useGridApiEventHandler(apiRef, GRID_DOUBLE_ROW_CLICK, options.onRowDoubleClick);
+ useGridApiEventHandler(apiRef, GRID_ROW_OVER, options.onRowOver);
+ useGridApiEventHandler(apiRef, GRID_ROW_OUT, options.onRowOut);
+ useGridApiEventHandler(apiRef, GRID_ROW_ENTER, options.onRowEnter);
+ useGridApiEventHandler(apiRef, GRID_ROW_LEAVE, options.onRowLeave);
- useGridApiEventHandler(apiRef, GRID_CELL_HOVER, options.onCellHover);
- useGridApiEventHandler(apiRef, GRID_ROW_HOVER, options.onRowHover);
useGridApiEventHandler(apiRef, GRID_COMPONENT_ERROR, options.onError);
useGridApiEventHandler(apiRef, GRID_STATE_CHANGE, options.onStateChange);
@@ -211,11 +121,7 @@ export function useEvents(gridRootRef: React.RefObject, apiRef:
const keyUpHandler = getHandler(GRID_KEYUP);
const gridRootElem = gridRootRef.current;
- gridRootElem.addEventListener(GRID_CLICK, onClickHandler, { capture: true });
- gridRootElem.addEventListener(GRID_DOUBLE_CLICK, onDoubleClickHandler, { capture: true });
- gridRootElem.addEventListener(GRID_MOUSE_HOVER, onHoverHandler, { capture: true });
gridRootElem.addEventListener(GRID_FOCUS_OUT, onFocusOutHandler);
-
gridRootElem.addEventListener(GRID_KEYDOWN, keyDownHandler);
gridRootElem.addEventListener(GRID_KEYUP, keyUpHandler);
apiRef.current.isInitialised = true;
@@ -224,8 +130,6 @@ export function useEvents(gridRootRef: React.RefObject, apiRef:
return () => {
logger.debug('Clearing all events listeners');
api.publishEvent(GRID_UNMOUNT);
- gridRootElem.removeEventListener(GRID_CLICK, onClickHandler, { capture: true });
- gridRootElem.removeEventListener(GRID_MOUSE_HOVER, onHoverHandler, { capture: true });
gridRootElem.removeEventListener(GRID_FOCUS_OUT, onFocusOutHandler);
gridRootElem.removeEventListener(GRID_KEYDOWN, keyDownHandler);
gridRootElem.removeEventListener(GRID_KEYUP, keyUpHandler);
@@ -233,15 +137,5 @@ export function useEvents(gridRootRef: React.RefObject, apiRef:
};
}
return undefined;
- }, [
- gridRootRef,
- apiRef.current?.isInitialised,
- getHandler,
- logger,
- onClickHandler,
- onDoubleClickHandler,
- onHoverHandler,
- onFocusOutHandler,
- apiRef,
- ]);
+ }, [gridRootRef, apiRef.current?.isInitialised, getHandler, logger, onFocusOutHandler, apiRef]);
}
diff --git a/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts b/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts
index 5e88e6d5e1e03..92e611bdbf1e6 100644
--- a/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts
+++ b/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts
@@ -5,7 +5,7 @@ import { useLogger } from '../utils/useLogger';
export function useGridApiEventHandler(
apiRef: GridApiRef,
eventName: string,
- handler?: (args: any) => void,
+ handler?: (...args: any) => void,
) {
const logger = useLogger('useGridApiEventHandler');
diff --git a/packages/grid/_modules_/grid/hooks/root/useGridApiMethod.ts b/packages/grid/_modules_/grid/hooks/root/useGridApiMethod.ts
index 26fae50738771..946667a046129 100644
--- a/packages/grid/_modules_/grid/hooks/root/useGridApiMethod.ts
+++ b/packages/grid/_modules_/grid/hooks/root/useGridApiMethod.ts
@@ -10,23 +10,18 @@ export function useGridApiMethod>(
) {
const logger = useLogger('useGridApiMethod');
const apiMethodsRef = React.useRef(apiMethods);
+ const [apiMethodsNames] = React.useState(Object.keys(apiMethods));
React.useEffect(() => {
apiMethodsRef.current = apiMethods;
}, [apiMethods]);
React.useEffect(() => {
- if (!apiRef.current.isInitialised) {
- return;
- }
-
- Object.keys(apiMethods).forEach((methodName) => {
+ apiMethodsNames.forEach((methodName) => {
if (!apiRef.current.hasOwnProperty(methodName)) {
logger.debug(`Adding ${apiName}.${methodName} to apiRef`);
- apiRef.current[methodName] = (...args) => {
- return apiMethodsRef.current[methodName](...args);
- };
+ apiRef.current[methodName] = (...args) => apiMethodsRef.current[methodName](...args);
}
});
- }, [apiMethods, apiName, apiRef, logger]);
+ }, [apiMethodsNames, apiName, apiRef, logger]);
}
diff --git a/packages/grid/_modules_/grid/models/api/gridApi.ts b/packages/grid/_modules_/grid/models/api/gridApi.ts
index 9204c3997af60..cfea6b26c03ca 100644
--- a/packages/grid/_modules_/grid/models/api/gridApi.ts
+++ b/packages/grid/_modules_/grid/models/api/gridApi.ts
@@ -1,5 +1,6 @@
import { ColumnMenuApi } from './columnMenuApi';
import { ColumnResizeApi } from './columnResizeApi';
+import { GridParamsApi } from './gridParamsApi';
import { ComponentsApi } from './gridComponentsApi';
import { FilterApi } from './filterApi';
import { GridEditRowApi } from './gridEditRowApi';
@@ -28,6 +29,7 @@ export type GridApi = GridCoreApi &
GridEventsApi &
GridRowApi &
GridEditRowApi &
+ GridParamsApi &
GridColumnApi &
ColumnReorderApi &
GridSelectionApi &
diff --git a/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts b/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts
index 0ee6b9e709602..9575f249df4ee 100644
--- a/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts
+++ b/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts
@@ -1,4 +1,4 @@
-import { GridCellMode, GridCellValue } from '../gridCell';
+import { GridCellMode } from '../gridCell';
import { GridEditRowsModel, GridEditRowUpdate } from '../gridEditRowModel';
import { GridRowId } from '../gridRows';
import { GridCellParams } from '../params/gridCellParams';
@@ -36,12 +36,6 @@ export interface GridEditRowApi {
* @param update
*/
commitCellChange: (id: GridRowId, update: GridEditRowUpdate) => void;
- /**
- * Get the cell value of a row and field.
- * @param id
- * @param field
- */
- getCellValue: (id: GridRowId, field: string) => GridCellValue;
/**
* Callback fired when the EditRowModel changed.
* @param handler
diff --git a/packages/grid/_modules_/grid/models/api/gridParamsApi.ts b/packages/grid/_modules_/grid/models/api/gridParamsApi.ts
new file mode 100644
index 0000000000000..35095dc3d812c
--- /dev/null
+++ b/packages/grid/_modules_/grid/models/api/gridParamsApi.ts
@@ -0,0 +1,48 @@
+import { GridCellValue } from '../gridCell';
+import { GridRowId } from '../gridRows';
+import { GridCellParams } from '../params/gridCellParams';
+import { GridColParams } from '../params/gridColParams';
+import { GridRowParams } from '../params/gridRowParams';
+
+export interface GridParamsApi {
+ /**
+ * Get the cell value of a row and field.
+ * @param id
+ * @param field
+ */
+ getCellValue: (id: GridRowId, field: string) => GridCellValue;
+ /**
+ * Get the cell DOM element.
+ * @param id
+ * @param field
+ */
+ getCellElement: (id: GridRowId, field: string) => HTMLDivElement | null;
+ /**
+ * Get the cell params that are passed in events.
+ * @param id
+ * @param field
+ */
+ getCellParams: (id: GridRowId, field: string) => GridCellParams;
+ /**
+ * Get the row params that are passed in events.
+ * @param id
+ * @param field
+ */
+ getRowParams: (id: GridRowId) => GridRowParams;
+ /**
+ * Get the row DOM element.
+ * @param id
+ * @param field
+ */
+ getRowElement: (id: GridRowId) => HTMLDivElement | null;
+ /**
+ * Get the column header DOM element.
+ * @param field
+ */
+ getColumnHeaderElement: (field: string) => HTMLDivElement | null;
+ /**
+ * Get the header params that are passed in events.
+ * @param field
+ */
+ getColumnHeaderParams: (field: string) => GridColParams;
+}
diff --git a/packages/grid/_modules_/grid/models/api/index.ts b/packages/grid/_modules_/grid/models/api/index.ts
index 032328ea976f8..ff15818a3ce10 100644
--- a/packages/grid/_modules_/grid/models/api/index.ts
+++ b/packages/grid/_modules_/grid/models/api/index.ts
@@ -1,4 +1,5 @@
export * from './gridApiRef';
+export * from './gridParamsApi';
export * from './gridCoreApi';
export * from './gridColumnApi';
export * from './gridComponentsApi';
diff --git a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts
index 3bd0888cb2587..d947351afb99c 100644
--- a/packages/grid/_modules_/grid/models/colDef/gridColDef.ts
+++ b/packages/grid/_modules_/grid/models/colDef/gridColDef.ts
@@ -2,7 +2,11 @@ import * as React from 'react';
import { GridCellValue } from '../gridCell';
import { GridCellClassNamePropType, GridCellClassRules } from '../gridCellClass';
import { GridFilterOperator } from '../gridFilterOperator';
-import { GridCellParams, ValueFormatterParams, ValueGetterParams } from '../params/gridCellParams';
+import {
+ GridCellParams,
+ GridValueFormatterParams,
+ GridValueGetterParams,
+} from '../params/gridCellParams';
import { GridColParams } from '../params/gridColParams';
import { GridComparatorFn } from '../gridSortModel';
import { GridColType, GridNativeColTypes } from './gridColType';
@@ -74,12 +78,12 @@ export interface GridColDef {
* Function that allows to get a specific data instead of field to render in the cell.
* @param params
*/
- valueGetter?: (params: ValueGetterParams) => GridCellValue;
+ valueGetter?: (params: GridValueGetterParams) => GridCellValue;
/**
* Function that allows to apply a formatter before rendering its value.
* @param params
*/
- valueFormatter?: (params: ValueFormatterParams) => GridCellValue;
+ valueFormatter?: (params: GridValueFormatterParams) => GridCellValue;
/**
* Class name that will be added in cells for that column.
*/
diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx
index cf13904586a69..89940fd6636ca 100644
--- a/packages/grid/_modules_/grid/models/gridOptions.tsx
+++ b/packages/grid/_modules_/grid/models/gridOptions.tsx
@@ -1,3 +1,4 @@
+import * as React from 'react';
import { GRID_DEFAULT_LOCALE_TEXT } from '../constants/localeTextConstants';
import { FilterModel } from '../hooks/features/filter/FilterModelState';
import { Logger } from '../hooks/utils/useLogger';
@@ -225,33 +226,75 @@ export interface GridOptions {
/**
* Callback fired when a click event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
+ * @param event [[React.MouseEvent]].
*/
- onCellClick?: (param: GridCellParams) => void;
+ onCellClick?: (param: GridCellParams, event: React.MouseEvent) => void;
/**
* Callback fired when a double click event comes from a cell element.
* @param param With all properties from [[CellParams]].
+ * @param event [[React.MouseEvent]].
*/
- onCellDoubleClick?: (param: GridCellParams) => void;
+ onCellDoubleClick?: (param: GridCellParams, event: React.MouseEvent) => void;
/**
- * Callback fired when a hover event comes from a cell element.
+ * Callback fired when a mouseover event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
+ * @param event [[React.MouseEvent]].
*/
- onCellHover?: (param: GridCellParams) => void;
+ onCellOver?: (param: GridCellParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouseout event comes from a cell element.
+ * @param param With all properties from [[GridCellParams]].
+ * @param event [[React.MouseEvent]].
+ */
+ onCellOut?: (param: GridCellParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouse enter event comes from a cell element.
+ * @param param With all properties from [[GridCellParams]].
+ * @param event [[React.MouseEvent]].
+ */
+ onCellEnter?: (param: GridCellParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouse leave event comes from a cell element.
+ * @param param With all properties from [[GridCellParams]].
+ * @param event [[React.MouseEvent]].
+ */
+ onCellLeave?: (param: GridCellParams, event: React.MouseEvent) => void;
/**
* Callback fired when a click event comes from a row container element.
* @param param With all properties from [[GridRowParams]].
+ * @param event [[React.MouseEvent]].
*/
- onRowClick?: (param: GridRowParams) => void;
+ onRowClick?: (param: GridRowParams, event: React.MouseEvent) => void;
/**
- * Callback fired when a click event comes from a row container element.
+ * Callback fired when a double click event comes from a row container element.
* @param param With all properties from [[RowParams]].
+ * @param event [[React.MouseEvent]].
+ */
+ onRowDoubleClick?: (param: GridRowParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouseover event comes from a row container element.
+ * @param param With all properties from [[GridRowParams]].
+ * @param event [[React.MouseEvent]].
+ */
+ onRowOver?: (param: GridRowParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouseout event comes from a row container element.
+ * @param param With all properties from [[GridRowParams]].
+ * @param event [[React.MouseEvent]].
+ */
+ onRowOut?: (param: GridRowParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouse enter event comes from a row container element.
+ * @param param With all properties from [[GridRowParams]].
+ * @param event [[React.MouseEvent]].
*/
- onRowDoubleClick?: (param: GridRowParams) => void;
+ onRowEnter?: (param: GridRowParams, event: React.MouseEvent) => void;
/**
- * Callback fired when a hover event comes from a row container element.
+ * Callback fired when a mouse leave event comes from a row container element.
* @param param With all properties from [[GridRowParams]].
+ * @param event [[React.MouseEvent]].
*/
- onRowHover?: (param: GridRowParams) => void;
+ onRowLeave?: (param: GridRowParams, event: React.MouseEvent) => void;
/**
* Callback fired when one row is selected.
* @param param With all properties from [[GridRowSelectedParams]].
@@ -266,7 +309,32 @@ export interface GridOptions {
* Callback fired when a click event comes from a column header element.
* @param param With all properties from [[GridColParams]].
*/
- onColumnHeaderClick?: (param: GridColParams) => void;
+ onColumnHeaderClick?: (param: GridColParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a double click event comes from a column header element.
+ * @param param With all properties from [[GridColParams]].
+ */
+ onColumnHeaderDoubleClick?: (param: GridColParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouseover event comes from a column header element.
+ * @param param With all properties from [[GridColParams]].
+ */
+ onColumnHeaderOver?: (param: GridColParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouseout event comes from a column header element.
+ * @param param With all properties from [[GridColParams]].
+ */
+ onColumnHeaderOut?: (param: GridColParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouse enter event comes from a column header element.
+ * @param param With all properties from [[GridColParams]].
+ */
+ onColumnHeaderEnter?: (param: GridColParams, event: React.MouseEvent) => void;
+ /**
+ * Callback fired when a mouse leave event comes from a column header element.
+ * @param param With all properties from [[GridColParams]].
+ */
+ onColumnHeaderLeave?: (param: GridColParams, event: React.MouseEvent) => void;
/**
* Callback fired when the sort model changes before a column is sorted.
* @param param With all properties from [[GridSortModelParams]].
diff --git a/packages/grid/_modules_/grid/models/params/gridCellParams.ts b/packages/grid/_modules_/grid/models/params/gridCellParams.ts
index de00904d825f0..9176c4423d547 100644
--- a/packages/grid/_modules_/grid/models/params/gridCellParams.ts
+++ b/packages/grid/_modules_/grid/models/params/gridCellParams.ts
@@ -1,14 +1,18 @@
import { GridCellValue } from '../gridCell';
-import { GridRowModel } from '../gridRows';
+import { GridRowId, GridRowModel } from '../gridRows';
/**
* Object passed as parameter in the column [[GridColDef]] cell renderer.
*/
export interface GridCellParams {
/**
- * The HTMLElement that triggered the event
+ * The grid row id.
*/
- element?: HTMLElement;
+ id: GridRowId;
+ /**
+ * The HTMLElement cell element.
+ */
+ element?: HTMLElement | null;
/**
* The column field of the cell that triggered the event
*/
@@ -17,6 +21,10 @@ export interface GridCellParams {
* The cell value, but if the column has valueGetter, use getValue.
*/
value: GridCellValue;
+ /**
+ * The cell value formatted with the column valueFormatter.
+ */
+ formattedValue: GridCellValue;
/**
* A function that let you get data from other columns.
* @param field
@@ -51,9 +59,9 @@ export interface GridCellParams {
/**
* Alias of GridCellParams.
*/
-export type ValueGetterParams = GridCellParams;
+export type GridValueGetterParams = Omit;
/**
* Alias of GridCellParams.
*/
-export type ValueFormatterParams = GridCellParams;
+export type GridValueFormatterParams = Omit;
diff --git a/packages/grid/_modules_/grid/models/params/gridColParams.ts b/packages/grid/_modules_/grid/models/params/gridColParams.ts
index e97bc6906d876..67c1c3e982edd 100644
--- a/packages/grid/_modules_/grid/models/params/gridColParams.ts
+++ b/packages/grid/_modules_/grid/models/params/gridColParams.ts
@@ -2,6 +2,10 @@
* Object passed as parameter in the column [[GridColDef]] header renderer.
*/
export interface GridColParams {
+ /**
+ * The HTMLElement column header element.
+ */
+ element?: HTMLElement | null;
/**
* The column field of the column that triggered the event
*/
diff --git a/packages/grid/_modules_/grid/models/params/gridRowParams.ts b/packages/grid/_modules_/grid/models/params/gridRowParams.ts
index 0d9fdec99320c..d733139176038 100644
--- a/packages/grid/_modules_/grid/models/params/gridRowParams.ts
+++ b/packages/grid/_modules_/grid/models/params/gridRowParams.ts
@@ -1,14 +1,18 @@
import { GridCellValue } from '../gridCell';
-import { GridRowModel } from '../gridRows';
+import { GridRowId, GridRowModel } from '../gridRows';
/**
* Object passed as parameter in the column [[GridColDef]] cell renderer.
*/
export interface GridRowParams {
/**
- * The HTMLElement that triggered the event
+ * The grid row id.
*/
- element?: HTMLElement;
+ id: GridRowId;
+ /**
+ * The HTMLElement row element.
+ */
+ element?: HTMLElement | null;
/**
* A function that let you get data from other columns.
* @param field
diff --git a/packages/grid/_modules_/grid/utils/domUtils.ts b/packages/grid/_modules_/grid/utils/domUtils.ts
index 1a45e1b3872d0..06361cbface09 100644
--- a/packages/grid/_modules_/grid/utils/domUtils.ts
+++ b/packages/grid/_modules_/grid/utils/domUtils.ts
@@ -5,6 +5,7 @@ import {
GRID_ROW_CSS_CLASS,
} from '../constants/cssClassesConstants';
import { GridCellIndexCoordinates } from '../models/gridCell';
+import { GridRowId } from '../models/gridRows';
export function isOverflown(element: Element): boolean {
return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
@@ -25,13 +26,6 @@ export function isGridCellRoot(elem: Element | null): boolean {
return elem != null && elem.classList.contains(GRID_CELL_CSS_CLASS);
}
-export function isGridCell(elem: Element | null): boolean {
- return (
- elem != null &&
- (isGridCellRoot(elem) || findParentElementFromClassName(elem, GRID_CELL_CSS_CLASS) !== null)
- );
-}
-
export function isGridHeaderTitleContainer(elem: Element): boolean {
return elem && findParentElementFromClassName(elem, GRID_HEADER_CELL_TITLE_CSS_CLASS) !== null;
}
@@ -40,10 +34,6 @@ export function getIdFromRowElem(rowEl: Element): string {
return rowEl.getAttribute('data-id')!;
}
-export function getFieldFromCellElem(cellEl: Element): string {
- return cellEl.getAttribute('data-field')!;
-}
-
export function getFieldFromHeaderElem(colCellEl: Element): string {
return colCellEl.getAttribute('data-field')!;
}
@@ -86,3 +76,19 @@ export function getGridCellElementFromIndexes(
`:scope .${GRID_CELL_CSS_CLASS}[aria-colIndex='${colIndex}'][data-rowIndex='${rowIndex}']`,
) as HTMLDivElement;
}
+
+export function getGridColumnHeaderElement(root: Element, field: string) {
+ return root.querySelector(`[role='columnheader'][data-field='${field}']`) as HTMLDivElement;
+}
+
+export function getGridRowElement(root: Element, id: GridRowId) {
+ return root.querySelector(`:scope .${GRID_ROW_CSS_CLASS}[data-id='${id}']`) as HTMLDivElement;
+}
+
+export function getGridCellElement(root: Element, { id, field }: { id: GridRowId; field: string }) {
+ const row = getGridRowElement(root, id);
+ if (!row) {
+ return null;
+ }
+ return row.querySelector(`.${GRID_CELL_CSS_CLASS}[data-field='${field}']`) as HTMLDivElement;
+}
diff --git a/packages/grid/_modules_/grid/utils/index.ts b/packages/grid/_modules_/grid/utils/index.ts
index 512fd9dafc275..077ff9ccdf409 100644
--- a/packages/grid/_modules_/grid/utils/index.ts
+++ b/packages/grid/_modules_/grid/utils/index.ts
@@ -4,7 +4,6 @@ export * from './domUtils';
export * from './classnames';
export * from './keyboardUtils';
export * from './mergeUtils';
-export * from './paramsUtils';
export * from './getGridLocalization';
export * from './material-ui-utils';
export * from './exportAs';
diff --git a/packages/grid/_modules_/grid/utils/paramsUtils.ts b/packages/grid/_modules_/grid/utils/paramsUtils.ts
deleted file mode 100644
index f2430026b1e09..0000000000000
--- a/packages/grid/_modules_/grid/utils/paramsUtils.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { GridCellValue } from '../models/gridCell';
-import { GridRowModel } from '../models/gridRows';
-import { GridColDef } from '../models/colDef/gridColDef';
-import { GridApi } from '../models/api/gridApi';
-import { GridCellParams } from '../models/params/gridCellParams';
-import { GridRowParams } from '../models/params/gridRowParams';
-
-let warnedOnce = false;
-
-export function buildGridCellParams({
- element,
- value,
- rowIndex,
- colIndex,
- rowModel,
- colDef,
- api,
-}: {
- rowModel: GridRowModel;
- colDef: GridColDef;
- rowIndex?: number;
- colIndex?: number;
- value: GridCellValue;
- api: GridApi;
- element?: HTMLElement;
-}): GridCellParams {
- const params: GridCellParams = {
- element,
- value,
- field: colDef?.field,
- getValue: (field: string) => {
- // We are getting the value of another column here, field
- const col = api.getColumnFromField(field);
-
- if (process.env.NODE_ENV !== 'production') {
- if (!col && !warnedOnce) {
- console.warn(
- [
- `Material-UI: You are calling getValue('${field}') but the column \`${field}\` is not defined.`,
- `Instead, you can access the data from \`params.row.${field}\`.`,
- ].join('\n'),
- );
- warnedOnce = true;
- }
- }
-
- if (!col || !col.valueGetter) {
- return rowModel[field];
- }
-
- return col.valueGetter(
- buildGridCellParams({
- value: rowModel[field],
- colDef: col,
- rowIndex,
- element,
- rowModel,
- api,
- }),
- );
- },
- row: rowModel,
- colDef,
- rowIndex,
- colIndex: colIndex || (colDef && api.getColumnIndex(colDef.field, true)),
- api,
- };
- const isEditableAttr = element && element.getAttribute('data-editable');
- params.isEditable =
- isEditableAttr != null ? isEditableAttr === 'true' : colDef && api.isCellEditable(params);
-
- return params;
-}
-
-export function buildGridRowParams({
- element,
- rowIndex,
- rowModel,
- api,
-}: {
- rowModel: GridRowModel;
- colDef: GridColDef;
- rowIndex: number;
- api: GridApi;
- element?: HTMLElement;
-}): GridRowParams {
- return {
- element,
- columns: api.getAllColumns(),
- getValue: (field: string) => rowModel[field],
- row: rowModel,
- rowIndex,
- api,
- };
-}
diff --git a/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx b/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx
index a951b953f8337..4e24305ff7f56 100644
--- a/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx
+++ b/packages/grid/data-grid/src/tests/layout.DataGrid.test.tsx
@@ -7,7 +7,7 @@ import {
} from 'test/utils';
import { useFakeTimers, stub } from 'sinon';
import { expect } from 'chai';
-import { DataGrid, ValueGetterParams, GridToolbar } from '@material-ui/data-grid';
+import { DataGrid, GridValueGetterParams, GridToolbar } from '@material-ui/data-grid';
import { getColumnValues, raf, sleep } from 'test/utils/helperFn';
describe(' - Layout & Warnings', () => {
@@ -170,7 +170,7 @@ describe(' - Layout & Warnings', () => {
{ field: 'id', hide: true },
{
field: 'fullName',
- valueGetter: (params: ValueGetterParams) => params.getValue('age'),
+ valueGetter: (params: GridValueGetterParams) => params.getValue('age'),
},
];
expect(() => {
diff --git a/packages/grid/x-grid/src/tests/events.XGrid.test.tsx b/packages/grid/x-grid/src/tests/events.XGrid.test.tsx
new file mode 100644
index 0000000000000..d051946d3bd3d
--- /dev/null
+++ b/packages/grid/x-grid/src/tests/events.XGrid.test.tsx
@@ -0,0 +1,220 @@
+import * as React from 'react';
+import {
+ createClientRenderStrictMode,
+ // @ts-ignore
+ fireEvent,
+} from 'test/utils';
+import { expect } from 'chai';
+import {
+ XGrid,
+ GridColParams,
+ useGridApiRef,
+ XGridProps,
+ GridRowParams,
+ GridCellParams,
+ GridRowsProp,
+ GridColumns,
+} from '@material-ui/x-grid';
+import { getCell, getColumnHeaderCell, getRow } from 'test/utils/helperFn';
+
+describe(' - Events Params ', () => {
+ // TODO v5: replace with createClientRender
+ const render = createClientRenderStrictMode();
+
+ const baselineProps: { rows: GridRowsProp; columns: GridColumns } = {
+ rows: [
+ {
+ id: 1,
+ first: 'Mike',
+ age: 11,
+ },
+ {
+ id: 2,
+ first: 'Jack',
+ age: 11,
+ },
+ {
+ id: 3,
+ first: 'Mike',
+ age: 20,
+ },
+ ],
+ columns: [
+ { field: 'id' },
+ { field: 'first', editable: true },
+ { field: 'age' },
+ {
+ field: 'firstAge',
+ valueGetter: (params) => `${params.row.first}_${params.row.age}`,
+ valueFormatter: (params) => `${params.value} yrs`,
+ },
+ ],
+ };
+
+ before(function beforeHook() {
+ if (/jsdom/.test(window.navigator.userAgent)) {
+ // Need layouting
+ this.skip();
+ }
+ });
+
+ let apiRef;
+ const TestEvents = (props: Partial) => {
+ apiRef = useGridApiRef();
+ return (
+
+
+
+ );
+ };
+
+ describe('columnHeaderParams', () => {
+ it('should include the correct params', () => {
+ let eventArgs: { params: GridColParams; event: React.MouseEvent } | null = null;
+ const handleClick = (params, event) => {
+ eventArgs = { params, event };
+ };
+ render();
+
+ const ageColumnElement = getColumnHeaderCell(3);
+ fireEvent.click(ageColumnElement);
+
+ expect(eventArgs!.params).to.deep.include({
+ colDef: apiRef!.current.getColumnFromField('age'),
+ element: ageColumnElement,
+ colIndex: 2,
+ field: 'age',
+ api: apiRef.current,
+ });
+ });
+ });
+
+ describe('RowsParams', () => {
+ it('should include the correct params', () => {
+ let eventArgs: { params: GridRowParams; event: React.MouseEvent } | null = null;
+
+ const handleClick = (params, event) => {
+ eventArgs = { params, event };
+ };
+ render();
+
+ const row1 = getRow(1);
+ fireEvent.click(row1);
+
+ expect(eventArgs!.params).to.deep.include({
+ id: 2,
+ element: row1,
+ row: baselineProps.rows[1],
+ rowIndex: 1,
+ columns: apiRef!.current.getAllColumns(),
+ api: apiRef.current,
+ });
+ });
+ });
+
+ describe('CellsParams', () => {
+ let eventArgs: { params: GridCellParams; event: React.MouseEvent } | null = null;
+ let cell11;
+
+ beforeEach(() => {
+ const handleClick = (params, event) => {
+ eventArgs = { params, event };
+ };
+ render();
+ });
+
+ it('should include the correct params', () => {
+ cell11 = getCell(1, 1);
+ fireEvent.click(cell11);
+
+ expect(eventArgs!.params).to.deep.include({
+ id: 2,
+ value: 'Jack',
+ formattedValue: 'Jack',
+ isEditable: true,
+ element: cell11,
+ row: baselineProps.rows[1],
+ rowIndex: 1,
+ colDef: apiRef!.current.getColumnFromField('first'),
+ colIndex: 1,
+ api: apiRef.current,
+ });
+ });
+
+ it('should consider value getter', () => {
+ const cellFirstAge = getCell(1, 3);
+ fireEvent.click(cellFirstAge);
+
+ expect(eventArgs!.params.value).to.equal('Jack_11');
+ });
+
+ it('should consider value formatter', () => {
+ const cellFirstAge = getCell(1, 3);
+ fireEvent.click(cellFirstAge);
+
+ expect(eventArgs!.params.formattedValue).to.equal('Jack_11 yrs');
+ });
+ });
+
+ describe('onCellClick', () => {
+ let eventStack: string[] = [];
+ const push = (name: string) => () => {
+ eventStack.push(name);
+ };
+
+ beforeEach(() => {
+ eventStack = [];
+ });
+
+ it('should bubble to the row', () => {
+ render();
+
+ const cell11 = getCell(1, 1);
+ fireEvent.click(cell11);
+ expect(eventStack).to.deep.equal(['cellClick', 'rowClick']);
+ });
+
+ it('should not bubble to the row if the column has disableEventBubbling', () => {
+ render(
+ ({
+ ...col,
+ disableClickEventBubbling: true,
+ }))}
+ />,
+ );
+
+ const cell11 = getCell(1, 1);
+ fireEvent.click(cell11);
+ expect(eventStack).to.deep.equal(['cellClick']);
+ });
+
+ it('should allow to stop propagation', () => {
+ const stopClick = (params, event) => {
+ event.stopPropagation();
+ };
+ render();
+
+ const cell11 = getCell(1, 1);
+ fireEvent.click(cell11);
+ expect(eventStack).to.deep.equal([]);
+ });
+
+ it('should select a row by default', () => {
+ render();
+
+ const cell11 = getCell(1, 1);
+ fireEvent.click(cell11);
+ expect(eventStack).to.deep.equal(['rowSelected']);
+ });
+
+ it('should not select a row if options.disableSelectionOnClick', () => {
+ render();
+ const cell11 = getCell(1, 1);
+ fireEvent.click(cell11);
+ expect(eventStack).to.deep.equal([]);
+ });
+ });
+});
diff --git a/packages/storybook/.storybook/preview.tsx b/packages/storybook/.storybook/preview.tsx
index b9f540b2b564f..e6b45512938c4 100644
--- a/packages/storybook/.storybook/preview.tsx
+++ b/packages/storybook/.storybook/preview.tsx
@@ -14,7 +14,12 @@ configureActions({
});
export const parameters = {
- actions: { argTypesRegex: '^on.*' },
+ // The storybook action panel is throwing a serialisation error with mouse events
+ // due to its circular structure
+ actions: {
+ argTypesRegex:
+ '^on((?!CellClick|CellDoubleClick|CellEnter|CellLeave|CellOut|CellOver|ColumnHeaderClick|ColumnHeaderDoubleClick|ColumnHeaderOver|ColumnHeaderOut|ColumnHeaderEnter|ColumnHeaderLeave|StateChange|RowClick|RowDoubleClick|RowEnter|RowLeave|RowOut|RowOver).)*$',
+ },
options: {
/**
* display the top-level grouping as a "root" in the sidebar
diff --git a/packages/storybook/src/stories/grid-columns.stories.tsx b/packages/storybook/src/stories/grid-columns.stories.tsx
index c5324dc2cd972..783eee43028b9 100644
--- a/packages/storybook/src/stories/grid-columns.stories.tsx
+++ b/packages/storybook/src/stories/grid-columns.stories.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { GridColDef, XGrid, GridColTypeDef } from '@material-ui/x-grid';
+import { GridColDef, XGrid, GridColTypeDef, GridValueGetterParams } from '@material-ui/x-grid';
import CreateIcon from '@material-ui/icons/Create';
import Button from '@material-ui/core/Button';
import { useData } from '../hooks/useData';
@@ -287,3 +287,32 @@ export const FlexColumnWidth2000 = () => {
);
};
+
+export const ValueGetterAndFormatter = () => {
+ const [data] = React.useState({
+ rows: [
+ { id: 1, first: 'mark', age: 1 },
+ { id: 2, first: 'jack', age: 2 },
+ ],
+ columns: [
+ { field: 'id', hide: true },
+ {
+ field: 'firstAge',
+ valueGetter: (params: GridValueGetterParams) =>
+ `${params.getValue('first')}_${params.getValue('age')}`,
+ },
+ {
+ field: 'firstAgeFormatted',
+ valueGetter: (params: GridValueGetterParams) =>
+ `${params.getValue('first')}_${params.getValue('age')}`,
+ valueFormatter: (params) => `${params.value} yrs`,
+ },
+ ],
+ });
+
+ return (
+