diff --git a/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.js b/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.js
index a0c9594fa202..5447ca4650a1 100644
--- a/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.js
+++ b/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.js
@@ -64,7 +64,7 @@ export default function ValidateServerNameGrid() {
});
}, 100);
- event.stopPropagation();
+ event.defaultMuiPrevented = true;
}
},
[apiRef],
diff --git a/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.tsx b/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.tsx
index 541e1a7370a8..bf0985e5803f 100644
--- a/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.tsx
+++ b/docs/src/pages/components/data-grid/editing/ValidateServerNameGrid.tsx
@@ -70,7 +70,7 @@ export default function ValidateServerNameGrid() {
});
}, 100);
- event.stopPropagation();
+ event.defaultMuiPrevented = true;
}
},
[apiRef],
diff --git a/docs/src/pages/components/data-grid/editing/editing.md b/docs/src/pages/components/data-grid/editing/editing.md
index aacf70ed9b31..95cc54710e2a 100644
--- a/docs/src/pages/components/data-grid/editing/editing.md
+++ b/docs/src/pages/components/data-grid/editing/editing.md
@@ -87,7 +87,7 @@ Server-side validation works like client-side [validation](#validation).
- Validate the value in the server.
- Once the server responds, set the `error` attribute to false if it is valid. This allows to commit it.
-**Note:** To prevent the default client-side behavior, use `event.stopPropagation()`.
+**Note:** To prevent the default client-side behavior, set `event.defaultMuiPrevented` to `true`.
This demo shows how you can validate a username asynchronously and prevent the user from committing the value while validating.
It's using `XGrid` but the same approach can be used with `DataGrid`.
@@ -104,7 +104,7 @@ The demo lets you edit the ratings by double-clicking the cell.
### Edit using external button [
](https://material-ui.com/store/items/material-ui-pro/)
-You can override the default [start editing](#start-editing) triggers using the `event.stopPropagation()` API on the synthetic React events.
+You can override the default [start editing](#start-editing) triggers using the [`event.defaultMuiPrevented`](/components/data-grid/events#disabling-the-default-behavior) on the synthetic React events.
{{"demo": "pages/components/data-grid/editing/StartEditButtonGrid.js", "bg": "inline", "disableAd": true}}
diff --git a/docs/src/pages/components/data-grid/events/DoubleClickWithCtrlToEdit.js b/docs/src/pages/components/data-grid/events/DoubleClickWithCtrlToEdit.js
new file mode 100644
index 000000000000..fa02f6faddc8
--- /dev/null
+++ b/docs/src/pages/components/data-grid/events/DoubleClickWithCtrlToEdit.js
@@ -0,0 +1,25 @@
+import * as React from 'react';
+import { DataGrid } from '@material-ui/data-grid';
+import { useDemoData } from '@material-ui/x-grid-data-generator';
+
+export default function DoubleClickWithCtrlToEdit() {
+ const { data } = useDemoData({
+ dataSet: 'Commodity',
+ rowLength: 100,
+ maxColumns: 6,
+ editable: true,
+ });
+
+ return (
+
+ {
+ if (!event.ctrlKey) {
+ event.defaultMuiPrevented = true;
+ }
+ }}
+ {...data}
+ />
+
+ );
+}
diff --git a/docs/src/pages/components/data-grid/events/DoubleClickWithCtrlToEdit.tsx b/docs/src/pages/components/data-grid/events/DoubleClickWithCtrlToEdit.tsx
new file mode 100644
index 000000000000..fa02f6faddc8
--- /dev/null
+++ b/docs/src/pages/components/data-grid/events/DoubleClickWithCtrlToEdit.tsx
@@ -0,0 +1,25 @@
+import * as React from 'react';
+import { DataGrid } from '@material-ui/data-grid';
+import { useDemoData } from '@material-ui/x-grid-data-generator';
+
+export default function DoubleClickWithCtrlToEdit() {
+ const { data } = useDemoData({
+ dataSet: 'Commodity',
+ rowLength: 100,
+ maxColumns: 6,
+ editable: true,
+ });
+
+ return (
+
+ {
+ if (!event.ctrlKey) {
+ event.defaultMuiPrevented = true;
+ }
+ }}
+ {...data}
+ />
+
+ );
+}
diff --git a/docs/src/pages/components/data-grid/events/events.md b/docs/src/pages/components/data-grid/events/events.md
index 533098ba25b3..161ae5ce68a8 100644
--- a/docs/src/pages/components/data-grid/events/events.md
+++ b/docs/src/pages/components/data-grid/events/events.md
@@ -8,7 +8,7 @@ title: Data Grid - Events
## Subscribing to events
-You can subscribe to one of the [events emitted](/components/data-grid/events/#catalog-of-events) by calling `apiRef.current.subscribeEvent()` with the name of the event and a handler. The handler will be called with two arguments: a object with information related to the event and, optionally, a `React.SyntheticEvent` object if the event was emitted by a DOM element.
+You can subscribe to one of the [events emitted](/components/data-grid/events/#catalog-of-events) by calling `apiRef.current.subscribeEvent()` with the name of the event and a handler. The handler will be called with two arguments: an object with information related to the event and a `MuiEvent` containing the DOM event or the React synthetic event, when available.
```tsx
/**
@@ -20,14 +20,33 @@ You can subscribe to one of the [events emitted](/components/data-grid/events/#c
*/
subscribeEvent: (
event: string,
- handler: (params: any, event?: React.SyntheticEvent) => void,
+ handler: (params: any, event: MuiEvent) => void,
options?: GridSubscribeEventOptions,
) => () => void;
```
The following demo shows how to subscribe to the `columnResize` event. Try it by resizing the columns.
-{{"demo": "pages/components/data-grid/events/SubscribeToEvents.js", "bg": "inline", "defaultCodeOpen": false}}
+{{"demo": "pages/components/data-grid/events/SubscribeToEvents.js", "bg": "inline"}}
+
+## Disabling the default behavior
+
+Depending on the use case, it might be necessary to disable the default action taken by an event.
+The `MuiEvent` passed to the event handler has a `defaultMuiPrevented` property to control when the default behavior can be executed or not.
+Set it to `true` to block the default handling of an event and implement your own.
+
+```tsx
+
) => {
+ event.defaultMuiPrevented = true;
+ }}
+/>
+```
+
+Usually, double clicking a cell will put it into [edit mode](/components/data-grid/editing/).
+The following example changes this behavior by also requiring CTRL to be pressed.
+
+{{"demo": "pages/components/data-grid/events/DoubleClickWithCtrlToEdit.js", "bg": "inline"}}
## Catalog of events
diff --git a/packages/grid/_modules_/grid/GridComponentProps.ts b/packages/grid/_modules_/grid/GridComponentProps.ts
index d6457fbfba77..886349a27791 100644
--- a/packages/grid/_modules_/grid/GridComponentProps.ts
+++ b/packages/grid/_modules_/grid/GridComponentProps.ts
@@ -2,7 +2,7 @@ import { GridState } from './hooks/features/core/gridState';
import { GridApiRef } from './models/api/gridApiRef';
import { GridColumns } from './models/colDef/gridColDef';
import { GridSlotsComponent } from './models/gridSlotsComponent';
-import { GridOptions } from './models/gridOptions';
+import { GridOptions, MuiEvent } from './models/gridOptions';
import { GridSlotsComponentsProps } from './models/gridSlotsComponentsProps';
import { GridStateChangeParams } from './models/params/gridStateChangeParams';
import { GridRowIdGetter, GridRowsProp } from './models/gridRows';
@@ -67,7 +67,7 @@ export interface GridComponentProps extends GridOptionsProp {
/**
* Set a callback fired when the state of the grid is updated.
*/
- onStateChange?: (params: GridStateChangeParams) => void; // We are overriding the handler in GridOptions to fix the params type and avoid the cycle dependency
+ onStateChange?: (params: GridStateChangeParams, event: MuiEvent<{}>) => void; // We are overriding the handler in GridOptions to fix the params type and avoid the cycle dependency
/**
* Set of rows of type [[GridRowsProp]].
*/
diff --git a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx
index 9fdc0b0d75a0..7e37b1c2cb09 100644
--- a/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx
+++ b/packages/grid/_modules_/grid/hooks/features/columnResize/useGridColumnResize.tsx
@@ -101,7 +101,7 @@ export const useGridColumnResize = (
});
};
- const handleResizeMouseUp = useEventCallback(() => {
+ const handleResizeMouseUp = useEventCallback((nativeEvent) => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
stopListening();
@@ -109,13 +109,17 @@ export const useGridColumnResize = (
clearTimeout(stopResizeEventTimeout.current);
stopResizeEventTimeout.current = setTimeout(() => {
- apiRef.current.publishEvent(GRID_COLUMN_RESIZE_STOP);
- apiRef.current.publishEvent(GRID_COLUMN_WIDTH_CHANGE, {
- element: colElementRef.current,
- colDef: colDefRef.current,
- api: apiRef,
- width: colDefRef.current?.width,
- });
+ apiRef.current.publishEvent(GRID_COLUMN_RESIZE_STOP, null, nativeEvent);
+ apiRef.current.publishEvent(
+ GRID_COLUMN_WIDTH_CHANGE,
+ {
+ element: colElementRef.current,
+ colDef: colDefRef.current,
+ api: apiRef,
+ width: colDefRef.current?.width,
+ },
+ nativeEvent,
+ );
});
logger.debug(
@@ -126,7 +130,7 @@ export const useGridColumnResize = (
const handleResizeMouseMove = useEventCallback((nativeEvent) => {
// Cancel move in case some other element consumed a mouseup event and it was not fired.
if (nativeEvent.buttons === 0) {
- handleResizeMouseUp();
+ handleResizeMouseUp(nativeEvent);
return;
}
@@ -137,12 +141,16 @@ export const useGridColumnResize = (
newWidth = Math.max(colDefRef.current?.minWidth!, newWidth);
updateWidth(newWidth);
- apiRef.current.publishEvent(GRID_COLUMN_RESIZE, {
- element: colElementRef.current,
- colDef: colDefRef.current,
- api: apiRef,
- width: newWidth,
- });
+ apiRef.current.publishEvent(
+ GRID_COLUMN_RESIZE,
+ {
+ element: colElementRef.current,
+ colDef: colDefRef.current,
+ api: apiRef,
+ width: newWidth,
+ },
+ nativeEvent,
+ );
});
const handleColumnResizeMouseDown = useEventCallback(
@@ -168,7 +176,7 @@ export const useGridColumnResize = (
) as HTMLDivElement;
logger.debug(`Start Resize on col ${colDef.field}`);
- apiRef.current.publishEvent(GRID_COLUMN_RESIZE_START, { field: colDef.field });
+ apiRef.current.publishEvent(GRID_COLUMN_RESIZE_START, { field: colDef.field }, event);
colDefRef.current = colDef;
colElementRef.current = apiRef.current!.columnHeadersElementRef?.current!.querySelector(
@@ -205,7 +213,7 @@ export const useGridColumnResize = (
clearTimeout(stopResizeEventTimeout.current);
stopResizeEventTimeout.current = setTimeout(() => {
- apiRef.current.publishEvent(GRID_COLUMN_RESIZE_STOP);
+ apiRef.current.publishEvent(GRID_COLUMN_RESIZE_STOP, null, nativeEvent);
});
logger.debug(
@@ -232,12 +240,16 @@ export const useGridColumnResize = (
newWidth = Math.max(colDefRef.current?.minWidth!, newWidth);
updateWidth(newWidth);
- apiRef.current.publishEvent(GRID_COLUMN_RESIZE, {
- element: colElementRef.current,
- colDef: colDefRef.current,
- api: apiRef,
- width: newWidth,
- });
+ apiRef.current.publishEvent(
+ GRID_COLUMN_RESIZE,
+ {
+ element: colElementRef.current,
+ colDef: colDefRef.current,
+ api: apiRef,
+ width: newWidth,
+ },
+ nativeEvent,
+ );
});
const handleTouchStart = useEventCallback((event) => {
@@ -266,7 +278,7 @@ export const useGridColumnResize = (
const colDef = apiRef.current.getColumn(field);
logger.debug(`Start Resize on col ${colDef.field}`);
- apiRef.current.publishEvent(GRID_COLUMN_RESIZE_START, { field });
+ apiRef.current.publishEvent(GRID_COLUMN_RESIZE_START, { field }, event);
colDefRef.current = colDef;
colElementRef.current = findHeaderElementFromField(
diff --git a/packages/grid/_modules_/grid/hooks/features/focus/useGridFocus.ts b/packages/grid/_modules_/grid/hooks/features/focus/useGridFocus.ts
index e48416de0db1..0e64a7983c44 100644
--- a/packages/grid/_modules_/grid/hooks/features/focus/useGridFocus.ts
+++ b/packages/grid/_modules_/grid/hooks/features/focus/useGridFocus.ts
@@ -105,7 +105,7 @@ export const useGridFocus = (apiRef: GridApiRef, props: Pick {
+ (event: DocumentEventMap['click']) => {
const isInsideFocusedCell = insideFocusedCell.current;
insideFocusedCell.current = false;
diff --git a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts
index 06b7e8197ef2..e710caa677b3 100644
--- a/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts
+++ b/packages/grid/_modules_/grid/hooks/features/keyboard/useGridKeyboard.ts
@@ -65,9 +65,6 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => {
if ((event.target as any).nodeType === 1 && !isGridCellRoot(event.target as Element)) {
return;
}
- if (event.isPropagationStopped()) {
- return;
- }
// Get the most recent params because the cell mode may have changed by another listener
const cellParams = apiRef.current.getCellParams(params.id, params.field);
@@ -110,9 +107,6 @@ export const useGridKeyboard = (apiRef: GridApiRef): void => {
if (!isGridHeaderCellRoot(event.target as HTMLElement)) {
return;
}
- if (event.isPropagationStopped()) {
- return;
- }
if (isSpaceKey(event.key) && isGridHeaderCellRoot(event.target as HTMLElement)) {
event.preventDefault();
}
diff --git a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts
index 21b98e31cee4..42271ec315ea 100644
--- a/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts
+++ b/packages/grid/_modules_/grid/hooks/features/rows/useGridEditRows.ts
@@ -49,6 +49,29 @@ export function useGridEditRows(
const [, setGridState, forceUpdate] = useGridState(apiRef);
const options = useGridSelector(apiRef, optionsSelector);
+ const commitPropsAndExit = (params: GridCellParams, event: MouseEvent | React.SyntheticEvent) => {
+ if (params.cellMode === 'view') {
+ return;
+ }
+ apiRef.current.commitCellChange(params, event);
+ apiRef.current.publishEvent(GRID_CELL_EDIT_EXIT, params, event);
+ };
+
+ const handleCellFocusOut = useEventCallback(
+ (params: GridCellParams, event: MouseEvent | React.SyntheticEvent) => {
+ commitPropsAndExit(params, event);
+ },
+ );
+
+ const handleColumnHeaderDragStart = useEventCallback((nativeEvent) => {
+ const { cell } = apiRef.current.getState().focus;
+ if (!cell) {
+ return;
+ }
+ const params = apiRef.current.getCellParams(cell.id, cell.field);
+ commitPropsAndExit(params, nativeEvent);
+ });
+
const setCellMode = React.useCallback(
(id, field, mode: GridCellMode) => {
const isInEditMode = apiRef.current.getCellMode(id, field) === 'edit';
@@ -81,32 +104,6 @@ export function useGridEditRows(
[apiRef, forceUpdate, logger, setGridState],
);
- const commitPropsAndExit = (params: GridCellParams) => {
- if (params.cellMode === 'view') {
- return;
- }
- apiRef.current.commitCellChange(params);
- apiRef.current.publishEvent(GRID_CELL_EDIT_EXIT, params);
- };
-
- const handleCellFocusOut = useEventCallback(
- (params: GridCellParams, event?: MouseEvent | React.SyntheticEvent) => {
- if (event && (event as any).defaultMuiPrevented) {
- return;
- }
- commitPropsAndExit(params);
- },
- );
-
- const handleColumnHeaderDragStart = useEventCallback(() => {
- const { cell } = apiRef.current.getState().focus;
- if (!cell) {
- return;
- }
- const params = apiRef.current.getCellParams(cell.id, cell.field);
- commitPropsAndExit(params);
- });
-
const getCellMode = React.useCallback(
(id, field) => {
const editState = apiRef.current.getState().editRows;
@@ -158,10 +155,7 @@ export function useGridEditRows(
);
const handleEditCellPropsChange = React.useCallback(
- (params: GridEditCellPropsParams, event?: React.SyntheticEvent) => {
- if (event?.isPropagationStopped()) {
- return;
- }
+ (params: GridEditCellPropsParams) => {
apiRef.current.setEditCellProps(params);
},
[apiRef],
@@ -183,7 +177,7 @@ export function useGridEditRows(
);
const commitCellChange = React.useCallback(
- (params: GridCommitCellChangeParams, event?: React.SyntheticEvent): boolean => {
+ (params: GridCommitCellChangeParams, event?: MouseEvent | React.SyntheticEvent): boolean => {
const { id, field } = params;
const model = apiRef.current.getEditRowsModel();
if (!model[id] || !model[id][field]) {
@@ -202,11 +196,7 @@ export function useGridEditRows(
);
const handleCellEditCommit = React.useCallback(
- (params: GridCommitCellChangeParams, event?: React.SyntheticEvent) => {
- if (event?.isPropagationStopped()) {
- return;
- }
-
+ (params: GridCommitCellChangeParams) => {
const { id, field } = params;
const model = apiRef.current.getEditRowsModel();
const { value } = model[id][field];
@@ -220,7 +210,7 @@ export function useGridEditRows(
const handleCellEditEnter = React.useCallback(
(params: GridCellParams, event: React.MouseEvent | React.KeyboardEvent) => {
- if (!params.isEditable || event.isPropagationStopped()) {
+ if (!params.isEditable) {
return;
}
@@ -251,7 +241,7 @@ export function useGridEditRows(
const handleCellKeyDown = React.useCallback(
(params: GridCellParams, event) => {
const { id, field, cellMode, isEditable } = params;
- if (!isEditable || event.isPropagationStopped()) {
+ if (!isEditable) {
return;
}
@@ -272,7 +262,7 @@ export function useGridEditRows(
return;
}
}
- if (isEditMode && !event.isPropagationStopped() && isCellExitEditModeKeys(event.key)) {
+ if (isEditMode && isCellExitEditModeKeys(event.key)) {
apiRef.current.publishEvent(GRID_CELL_EDIT_EXIT, params, event);
}
},
@@ -281,7 +271,6 @@ export function useGridEditRows(
const handleCellEditExit = React.useCallback(
(params: GridCellParams, event?: React.SyntheticEvent) => {
- // TODO check if its propagation was stopped
setCellMode(params.id, params.field, 'view');
// When dispatched by the document, the event is not passed
diff --git a/packages/grid/_modules_/grid/hooks/root/useApi.ts b/packages/grid/_modules_/grid/hooks/root/useApi.ts
index 91637e88901b..f30fffbce702 100644
--- a/packages/grid/_modules_/grid/hooks/root/useApi.ts
+++ b/packages/grid/_modules_/grid/hooks/root/useApi.ts
@@ -4,15 +4,26 @@ import { GridSubscribeEventOptions } from '../../utils/eventEmitter/GridEventEmi
import { useLogger } from '../utils/useLogger';
import { GRID_COMPONENT_ERROR, GRID_UNMOUNT } from '../../constants/eventsConstants';
import { useGridApiMethod } from './useGridApiMethod';
+import { MuiEvent } from '../../models/gridOptions';
+
+const isSynthenticEvent = (event: any): event is React.SyntheticEvent => {
+ return event.isPropagationStopped !== undefined;
+};
export function useApi(apiRef: GridApiRef): void {
const logger = useLogger('useApi');
const publishEvent = React.useCallback(
- (name: string, params: any, event?: React.SyntheticEvent) => {
- if (!event || !event.isPropagationStopped || !event.isPropagationStopped()) {
- apiRef.current.emit(name, params, event);
+ (
+ name: string,
+ params: any,
+ event: MuiEvent = {},
+ ) => {
+ event.defaultMuiPrevented = false;
+ if (event && isSynthenticEvent(event) && event.isPropagationStopped()) {
+ return;
}
+ apiRef.current.emit(name, params, event);
},
[apiRef],
);
diff --git a/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts b/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts
index 25c13aceff45..51fdcae413f4 100644
--- a/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts
+++ b/packages/grid/_modules_/grid/hooks/root/useGridApiEventHandler.ts
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { MuiEvent } from '../../models/gridOptions';
import { GridApiRef } from '../../models/api/gridApiRef';
import { useLogger } from '../utils/useLogger';
@@ -12,7 +13,15 @@ export function useGridApiEventHandler(
React.useEffect(() => {
if (handler && eventName) {
- return apiRef.current.subscribeEvent(eventName, handler, options);
+ const enhancedHandler = (
+ params: any,
+ event: MuiEvent,
+ ) => {
+ if (!event.defaultMuiPrevented) {
+ handler(params, event);
+ }
+ };
+ return apiRef.current.subscribeEvent(eventName, enhancedHandler, options);
}
return undefined;
diff --git a/packages/grid/_modules_/grid/models/api/gridCoreApi.ts b/packages/grid/_modules_/grid/models/api/gridCoreApi.ts
index 761a96003bdd..727835f1a625 100644
--- a/packages/grid/_modules_/grid/models/api/gridCoreApi.ts
+++ b/packages/grid/_modules_/grid/models/api/gridCoreApi.ts
@@ -1,4 +1,5 @@
import * as React from 'react';
+import { MuiEvent } from '../gridOptions';
import {
GridEventEmitter,
GridSubscribeEventOptions,
@@ -52,7 +53,10 @@ export interface GridCoreApi extends GridEventEmitter {
*/
subscribeEvent: (
event: string,
- handler: (params: any, event?: React.SyntheticEvent) => void,
+ handler: (
+ params: any,
+ event: MuiEvent,
+ ) => void,
options?: GridSubscribeEventOptions,
) => () => void;
/**
@@ -60,7 +64,11 @@ export interface GridCoreApi extends GridEventEmitter {
* @param {string} name The name of the event.
* @param {...*} args Arguments to be passed to the handlers.
*/
- publishEvent: (name: string, ...args: any[]) => void;
+ publishEvent: (
+ name: string,
+ params?: any,
+ event?: MuiEvent,
+ ) => void;
/**
* Displays the error overlay component.
* @param {any} props Props to be passed to the `ErrorOverlay` component.
diff --git a/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts b/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts
index bae5946a536a..d9b99e458fdf 100644
--- a/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts
+++ b/packages/grid/_modules_/grid/models/api/gridEditRowApi.ts
@@ -60,5 +60,8 @@ export interface GridEditRowApi {
* @param {React.SyntheticEvent} event The event to pass forward.
* @returns {boolean} A boolean indicating if there is an error.
*/
- commitCellChange: (params: GridCommitCellChangeParams, event?: React.SyntheticEvent) => boolean;
+ commitCellChange: (
+ params: GridCommitCellChangeParams,
+ event?: MouseEvent | React.SyntheticEvent,
+ ) => boolean;
}
diff --git a/packages/grid/_modules_/grid/models/gridOptions.tsx b/packages/grid/_modules_/grid/models/gridOptions.tsx
index e9703b035185..54ad55c33018 100644
--- a/packages/grid/_modules_/grid/models/gridOptions.tsx
+++ b/packages/grid/_modules_/grid/models/gridOptions.tsx
@@ -27,7 +27,7 @@ import { GridColumnResizeParams } from './params/gridColumnResizeParams';
import { GridColumnVisibilityChangeParams } from './params/gridColumnVisibilityChangeParams';
import { GridClasses } from './gridClasses';
-export type MuiEvent = (E | React.SyntheticEvent) & {
+export type MuiEvent = E & {
defaultMuiPrevented?: boolean;
};
@@ -206,15 +206,21 @@ export interface GridOptions {
/**
* Callback fired when the edit cell value changes.
* @param {GridEditCellPropsParams} params With all properties from [[GridEditCellPropsParams]].
- * @param {React.SyntheticEvent} event The event that caused this prop to be called.
+ * @param {MuiEvent} event The event that caused this prop to be called.
*/
- onEditCellPropsChange?: (params: GridEditCellPropsParams, event?: React.SyntheticEvent) => void;
+ onEditCellPropsChange?: (
+ params: GridEditCellPropsParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when the cell changes are committed.
* @param {GridCellEditCommitParams} params With all properties from [[GridCellEditCommitParams]].
- * @param {React.SyntheticEvent} event The event that caused this prop to be called.
+ * @param {MuiEvent} event The event that caused this prop to be called.
*/
- onCellEditCommit?: (params: GridCellEditCommitParams, event?: React.SyntheticEvent) => void;
+ onCellEditCommit?: (
+ params: GridCellEditCommitParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when an exception is thrown in the grid, or when the `showError` API method is called.
*/
@@ -222,123 +228,150 @@ export interface GridOptions {
/**
* Callback fired when the active element leaves a cell.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.FocusEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellBlur?: (params: GridCellParams, event: React.FocusEvent) => void;
+ onCellBlur?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a click event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellClick?: (params: GridCellParams, event: React.MouseEvent) => void;
+ onCellClick?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a double click event comes from a cell element.
* @param param With all properties from [[CellParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellDoubleClick?: (params: GridCellParams, event: React.MouseEvent) => void;
+ onCellDoubleClick?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a cell loses focus.
* @param param With all properties from [[GridCellParams]].
- * @param event [[Event]].
+ * @param event [[MuiEvent]].
*/
- onCellFocusOut?: (params: GridCellParams, event?: MuiEvent) => void;
+ onCellFocusOut?: (
+ params: GridCellParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a keydown event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.KeyboardEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellKeyDown?: (params: GridCellParams, event: React.KeyboardEvent) => void;
+ onCellKeyDown?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a mouseover event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellOver?: (params: GridCellParams, event: React.MouseEvent) => void;
+ onCellOver?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a mouseout event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellOut?: (params: GridCellParams, event: React.MouseEvent) => void;
+ onCellOut?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a mouse enter event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellEnter?: (params: GridCellParams, event: React.MouseEvent) => void;
+ onCellEnter?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when a mouse leave event comes from a cell element.
* @param param With all properties from [[GridCellParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onCellLeave?: (params: GridCellParams, event: React.MouseEvent) => void;
+ onCellLeave?: (params: GridCellParams, event: MuiEvent) => void;
/**
* Callback fired when the cell mode changed.
* @param handler
+ * @param event [[MuiEvent<{}>]].
*/
- onCellModeChange?: (params: GridCellModeChangeParams) => void;
+ onCellModeChange?: (params: GridCellModeChangeParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when the cell value changed.
* @param handler
+ * @param event [[MuiEvent<{}>]].
*/
- onCellValueChange?: (params: GridEditCellValueParams) => void;
+ onCellValueChange?: (params: GridEditCellValueParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when a click event comes from a column header element.
* @param param With all properties from [[GridColumnHeaderParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onColumnHeaderClick?: (param: GridColumnHeaderParams, event: React.MouseEvent) => void;
+ onColumnHeaderClick?: (
+ param: GridColumnHeaderParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a double click event comes from a column header element.
* @param param With all properties from [[GridColumnHeaderParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onColumnHeaderDoubleClick?: (param: GridColumnHeaderParams, event: React.MouseEvent) => void;
+ onColumnHeaderDoubleClick?: (
+ param: GridColumnHeaderParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a mouseover event comes from a column header element.
* @param param With all properties from [[GridColumnHeaderParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onColumnHeaderOver?: (param: GridColumnHeaderParams, event: React.MouseEvent) => void;
+ onColumnHeaderOver?: (
+ param: GridColumnHeaderParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a mouseout event comes from a column header element.
* @param param With all properties from [[GridColumnHeaderParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onColumnHeaderOut?: (param: GridColumnHeaderParams, event: React.MouseEvent) => void;
+ onColumnHeaderOut?: (
+ param: GridColumnHeaderParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a mouse enter event comes from a column header element.
* @param param With all properties from [[GridColumnHeaderParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onColumnHeaderEnter?: (param: GridColumnHeaderParams, event: React.MouseEvent) => void;
+ onColumnHeaderEnter?: (
+ param: GridColumnHeaderParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a mouse leave event comes from a column header element.
* @param param With all properties from [[GridColumnHeaderParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onColumnHeaderLeave?: (param: GridColumnHeaderParams, event: React.MouseEvent) => void;
+ onColumnHeaderLeave?: (
+ param: GridColumnHeaderParams,
+ event: MuiEvent,
+ ) => void;
/**
* Callback fired when a column is reordered.
* @param param With all properties from [[GridColumnHeaderParams]].
+ * @param event [[MuiEvent<{}>]].
*/
- onColumnOrderChange?: (param: GridColumnOrderChangeParams) => void;
+ onColumnOrderChange?: (param: GridColumnOrderChangeParams, event: MuiEvent<{}>) => void;
/**
* Callback fired while a column is being resized.
* @param param With all properties from [[GridColumnResizeParams]].
+ * @param event [[MuiEvent<{}>]].
*/
- onColumnResize?: (param: GridColumnResizeParams) => void;
+ onColumnResize?: (param: GridColumnResizeParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when the width of a column is changed.
* @param param With all properties from [[GridColumnResizeParams]].
+ * @param event [[MuiEvent<{}>]].
*/
- onColumnWidthChange?: (param: GridColumnResizeParams) => void;
+ onColumnWidthChange?: (param: GridColumnResizeParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when a column visibility changes.
* @param param With all properties from [[GridColumnVisibilityChangeParams]].
+ * @param event [[MuiEvent<{}>]].
*/
- onColumnVisibilityChange?: (param: GridColumnVisibilityChangeParams) => void;
+ onColumnVisibilityChange?: (param: GridColumnVisibilityChangeParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when the Filter model changes before the filters are applied.
* @param model With all properties from [[GridFilterModel]].
@@ -357,49 +390,51 @@ export interface GridOptions {
/**
* Callback fired when a click event comes from a row container element.
* @param param With all properties from [[GridRowParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onRowClick?: (param: GridRowParams, event: React.MouseEvent) => void;
+ onRowClick?: (param: GridRowParams, event: MuiEvent) => void;
/**
* Callback fired when scrolling to the bottom of the grid viewport.
+ * @param event [[MuiEvent<{}>]].
* @param param
*/
- onRowsScrollEnd?: (params: GridRowScrollEndParams) => void;
+ onRowsScrollEnd?: (params: GridRowScrollEndParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when a double click event comes from a row container element.
* @param param With all properties from [[RowParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onRowDoubleClick?: (param: GridRowParams, event: React.MouseEvent) => void;
+ onRowDoubleClick?: (param: GridRowParams, event: MuiEvent) => void;
/**
* Callback fired when a mouseover event comes from a row container element.
* @param param With all properties from [[GridRowParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onRowOver?: (param: GridRowParams, event: React.MouseEvent) => void;
+ onRowOver?: (param: GridRowParams, event: MuiEvent) => void;
/**
* Callback fired when a mouseout event comes from a row container element.
* @param param With all properties from [[GridRowParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onRowOut?: (param: GridRowParams, event: React.MouseEvent) => void;
+ onRowOut?: (param: GridRowParams, event: MuiEvent) => 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]].
+ * @param event [[MuiEvent]].
*/
- onRowEnter?: (param: GridRowParams, event: React.MouseEvent) => void;
+ onRowEnter?: (param: GridRowParams, event: MuiEvent) => void;
/**
* Callback fired when a mouse leave event comes from a row container element.
* @param param With all properties from [[GridRowParams]].
- * @param event [[React.MouseEvent]].
+ * @param event [[MuiEvent]].
*/
- onRowLeave?: (param: GridRowParams, event: React.MouseEvent) => void;
+ onRowLeave?: (param: GridRowParams, event: MuiEvent) => void;
/**
* Callback fired when the grid is resized.
* @param param With all properties from [[GridResizeParams]].
+ * @param event [[MuiEvent<{}>]].
*/
- onResize?: (param: GridResizeParams) => void;
+ onResize?: (param: GridResizeParams, event: MuiEvent<{}>) => void;
/**
* Callback fired when the selection state of one or multiple rows changes.
* @param selectionModel With all the row ids [[GridRowId]][].
@@ -413,7 +448,7 @@ export interface GridOptions {
/**
* Callback fired when the state of the grid is updated.
*/
- onStateChange?: (params: any) => void;
+ onStateChange?: (params: any, event: MuiEvent<{}>) => void;
/**
* Set the current page.
* @default 1
diff --git a/packages/grid/x-grid/src/tests/editRows.XGrid.test.tsx b/packages/grid/x-grid/src/tests/editRows.XGrid.test.tsx
index 93055a932c67..42f4a7e135ec 100644
--- a/packages/grid/x-grid/src/tests/editRows.XGrid.test.tsx
+++ b/packages/grid/x-grid/src/tests/editRows.XGrid.test.tsx
@@ -101,7 +101,11 @@ describe(' - Edit Rows', () => {
});
it('should allow to stop double click using stopPropagation', () => {
- render( event.stopPropagation()} />);
+ render(
+ (event as React.SyntheticEvent).stopPropagation()}
+ />,
+ );
const cell = getCell(1, 0);
cell.focus();
fireEvent.doubleClick(cell);
@@ -177,7 +181,7 @@ describe(' - Edit Rows', () => {
code: 1,
target: cell,
isPropagationStopped: () => false,
- });
+ } as any);
// fireEvent.keyDown(cell, { key: 'a', code: 1, target: cell });
expect(cell).to.have.class('MuiDataGrid-cell--editable');
@@ -330,28 +334,6 @@ describe(' - Edit Rows', () => {
expect(getActiveCell()).to.equal('2-1');
});
- it('should the focus to the new field', () => {
- const handleCellBlur = (params, event) => {
- if (params.cellMode === 'edit') {
- event?.stopPropagation();
- }
- };
- render();
- // Turn first cell into edit mode
- apiRef!.current.setCellMode(0, 'brand', 'edit');
-
- // Turn second cell into edit mode
- getCell(1, 0).focus();
- apiRef!.current.setCellMode(1, 'brand', 'edit');
- expect(document.querySelectorAll('input').length).to.equal(2);
-
- // Try to focus the first cell's input
- const input0 = getCell(0, 0).querySelector('input');
- input0!.focus();
- fireEvent.click(input0);
- expect(document.activeElement).to.have.property('value', 'Nike');
- });
-
it('should apply the valueParser before saving the value', () => {
const valueParser = stub().withArgs('62').returns(1962);
render(
diff --git a/packages/grid/x-grid/src/tests/events.XGrid.test.tsx b/packages/grid/x-grid/src/tests/events.XGrid.test.tsx
index 0d25316d7f21..d32f5c7dd02c 100644
--- a/packages/grid/x-grid/src/tests/events.XGrid.test.tsx
+++ b/packages/grid/x-grid/src/tests/events.XGrid.test.tsx
@@ -17,6 +17,7 @@ import {
GridRowsProp,
GridColumns,
GRID_ROWS_SCROLL,
+ GRID_CELL_CSS_CLASS,
} from '@material-ui/x-grid';
import { getCell, getColumnHeaderCell, getRow } from 'test/utils/helperFn';
import { spy } from 'sinon';
@@ -218,6 +219,30 @@ describe(' - Events Params', () => {
expect(eventStack).to.deep.equal([]);
});
+ it('should allow to prevent the default behavior', () => {
+ const preventDefault = (params, event) => {
+ event.defaultMuiPrevented = true;
+ };
+ render();
+ const cell = getCell(1, 1);
+ fireEvent.doubleClick(cell);
+ expect(cell).not.to.have.class(`${GRID_CELL_CSS_CLASS}--editing`);
+ });
+
+ it('should allow to prevent the default behavior while allowing the event to propagate', () => {
+ const preventDefault = (params, event) => {
+ event.defaultMuiPrevented = true;
+ };
+ render();
+ const cell = getCell(1, 1);
+ cell.focus();
+ fireEvent.doubleClick(cell);
+ const input = cell.querySelector('input')!;
+ fireEvent.change(input, { target: { value: 'Lisa' } });
+ fireEvent.keyDown(input, { key: 'Enter' });
+ expect(cell).to.have.text('Jack');
+ });
+
it('should select a row by default', () => {
const handleSelection = spy();
render();
diff --git a/packages/storybook/src/stories/grid-events.stories.tsx b/packages/storybook/src/stories/grid-events.stories.tsx
index 04c115ae4772..a9c2a649f70f 100644
--- a/packages/storybook/src/stories/grid-events.stories.tsx
+++ b/packages/storybook/src/stories/grid-events.stories.tsx
@@ -73,7 +73,7 @@ export const OnCellClickNotPropagated = () => {
const data = useData(2000, 200);
const options: GridOptionsProp = {
onCellClick: (params, event) => {
- event.stopPropagation();
+ (event as React.SyntheticEvent).stopPropagation();
action('cell click')(params);
},
};
diff --git a/packages/storybook/src/stories/grid-rows.stories.tsx b/packages/storybook/src/stories/grid-rows.stories.tsx
index 5229e430e628..9a68be194355 100644
--- a/packages/storybook/src/stories/grid-rows.stories.tsx
+++ b/packages/storybook/src/stories/grid-rows.stories.tsx
@@ -483,11 +483,11 @@ export function EditRowsControl() {
}, []);
const onCellEditCommit = React.useCallback(
- (params: GridCellEditCommitParams, event?: React.SyntheticEvent) => {
+ (params: GridCellEditCommitParams, event: MuiEvent) => {
const { id, field, value } = params;
- event!.persist();
+ event.persist();
// we stop propagation as we want to switch back to view mode after we updated the value on the server
- event!.stopPropagation();
+ event.defaultMuiPrevented = true;
let cellUpdate: any = { id };
cellUpdate[field] = value;
@@ -607,12 +607,12 @@ export function ValidateEditValueWithApiRefGrid() {
const classes = useEditCellStyles();
const onEditCellPropsChange = React.useCallback(
- ({ id, field, props }: GridEditCellPropsParams, event?: React.SyntheticEvent) => {
+ ({ id, field, props }: GridEditCellPropsParams, event: MuiEvent) => {
if (field === 'email') {
const isValid = validateEmail(props.value);
apiRef.current.setEditCellProps({ id, field, props: { ...props, error: !isValid } });
// Prevent the native behavior.
- event?.stopPropagation();
+ event.defaultMuiPrevented = true;
}
},
[apiRef],
@@ -676,7 +676,10 @@ export function ValidateEditValueServerSide() {
const keyStrokeTimeoutRef = React.useRef();
const handleCellEditPropChange = React.useCallback(
- async ({ id, field, props }: GridEditCellPropsParams, event) => {
+ async (
+ { id, field, props }: GridEditCellPropsParams,
+ event: MuiEvent,
+ ) => {
if (field === 'username') {
// TODO refactor this block
clearTimeout(promiseTimeout);
@@ -688,7 +691,7 @@ export function ValidateEditValueServerSide() {
apiRef.current.setEditCellProps({ id, field, props: { ...props, error: !isValid } });
}, 200);
- event.stopPropagation();
+ event.defaultMuiPrevented = true;
}
},
[apiRef],
@@ -742,25 +745,28 @@ export function EditCellUsingExternalButtonGrid() {
}, []);
const handleDoubleCellClick = React.useCallback(
- (params: GridCellParams, event: React.SyntheticEvent) => {
- event.stopPropagation();
+ (params: GridCellParams, event: MuiEvent) => {
+ event.defaultMuiPrevented = true;
},
[],
);
// Prevent from rolling back on escape
- const handleCellKeyDown = React.useCallback((params, event: React.KeyboardEvent) => {
+ const handleCellKeyDown = React.useCallback((params, event: MuiEvent) => {
if (['Escape', 'Delete', 'Backspace', 'Enter'].includes(event.key)) {
- event.stopPropagation();
+ event.defaultMuiPrevented = true;
}
}, []);
// Prevent from committing on focus out
- const handleCellFocusOut = React.useCallback((params, event?: MuiEvent) => {
- if (params.cellMode === 'edit' && event) {
- event.defaultMuiPrevented = true;
- }
- }, []);
+ const handleCellFocusOut = React.useCallback(
+ (params, event: MuiEvent) => {
+ if (params.cellMode === 'edit' && event) {
+ event.defaultMuiPrevented = true;
+ }
+ },
+ [],
+ );
return (
@@ -807,13 +813,13 @@ export function EditCellWithCellClickGrid() {
const apiRef = useGridApiRef();
const handleCellClick = React.useCallback(
- (params: GridCellParams, event) => {
+ (params: GridCellParams, event: MuiEvent) => {
// Or you can use the editRowModel prop, but I find it easier
// apiRef.current.setCellMode(params.id, params.field, 'edit');
apiRef.current.publishEvent(GRID_CELL_EDIT_ENTER, params, event);
// if I want to prevent selection I can do
- event.stopPropagation();
+ event.defaultMuiPrevented = true;
},
[apiRef],
);
@@ -832,16 +838,13 @@ export function EditCellWithMessageGrid() {
const [message, setMessage] = React.useState('');
React.useEffect(() => {
- return apiRef.current.subscribeEvent(
- GRID_CELL_EDIT_ENTER,
- (params: GridCellParams, event?: React.SyntheticEvent) => {
- setMessage(`Editing cell with value: ${params.value} at row: ${params.id}, column: ${
- params.field
- },
- triggered by ${event!.type}
- `);
+ return apiRef.current.subscribeEvent(GRID_CELL_EDIT_ENTER, (params: GridCellParams, event) => {
+ setMessage(`Editing cell with value: ${params.value} at row: ${params.id}, column: ${
+ params.field
},
- );
+ triggered by ${(event as React.SyntheticEvent)!.type}
+ `);
+ });
}, [apiRef]);
React.useEffect(() => {