diff --git a/docs/pages/api-docs/data-grid/data-grid-pro.json b/docs/pages/api-docs/data-grid/data-grid-pro.json
index ebdbd56250275..12bd2de04e4ba 100644
--- a/docs/pages/api-docs/data-grid/data-grid-pro.json
+++ b/docs/pages/api-docs/data-grid/data-grid-pro.json
@@ -312,6 +312,7 @@
"columnHeaderDropZone",
"columnHeaderTitle",
"columnHeaderTitleContainer",
+ "columnHeaderTitleContainerContent",
"columnHeaders",
"columnHeadersInner",
"columnHeadersInner--scrollable",
diff --git a/docs/pages/api-docs/data-grid/data-grid.json b/docs/pages/api-docs/data-grid/data-grid.json
index 26992b12e18f6..91db2cefd626a 100644
--- a/docs/pages/api-docs/data-grid/data-grid.json
+++ b/docs/pages/api-docs/data-grid/data-grid.json
@@ -259,6 +259,7 @@
"columnHeaderDropZone",
"columnHeaderTitle",
"columnHeaderTitleContainer",
+ "columnHeaderTitleContainerContent",
"columnHeaders",
"columnHeadersInner",
"columnHeadersInner--scrollable",
diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json
index bf98f6da8b08e..175fe126a6563 100644
--- a/docs/pages/x/api/data-grid/data-grid-pro.json
+++ b/docs/pages/x/api/data-grid/data-grid-pro.json
@@ -312,6 +312,7 @@
"columnHeaderDropZone",
"columnHeaderTitle",
"columnHeaderTitleContainer",
+ "columnHeaderTitleContainerContent",
"columnHeaders",
"columnHeadersInner",
"columnHeadersInner--scrollable",
diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json
index 6fd1b72c7863f..46f49557431fd 100644
--- a/docs/pages/x/api/data-grid/data-grid.json
+++ b/docs/pages/x/api/data-grid/data-grid.json
@@ -259,6 +259,7 @@
"columnHeaderDropZone",
"columnHeaderTitle",
"columnHeaderTitleContainer",
+ "columnHeaderTitleContainerContent",
"columnHeaders",
"columnHeadersInner",
"columnHeadersInner--scrollable",
diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json
index 2093ad9cb269b..3922fb93572c7 100644
--- a/docs/translations/api-docs/data-grid/data-grid-pro-pt.json
+++ b/docs/translations/api-docs/data-grid/data-grid-pro-pt.json
@@ -240,6 +240,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column header's title container element"
},
+ "columnHeaderTitleContainerContent": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the column header's title excepted buttons"
+ },
"columnHeaders": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column headers"
diff --git a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json
index 2093ad9cb269b..3922fb93572c7 100644
--- a/docs/translations/api-docs/data-grid/data-grid-pro-zh.json
+++ b/docs/translations/api-docs/data-grid/data-grid-pro-zh.json
@@ -240,6 +240,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column header's title container element"
},
+ "columnHeaderTitleContainerContent": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the column header's title excepted buttons"
+ },
"columnHeaders": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column headers"
diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json
index 2093ad9cb269b..3922fb93572c7 100644
--- a/docs/translations/api-docs/data-grid/data-grid-pro.json
+++ b/docs/translations/api-docs/data-grid/data-grid-pro.json
@@ -240,6 +240,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column header's title container element"
},
+ "columnHeaderTitleContainerContent": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the column header's title excepted buttons"
+ },
"columnHeaders": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column headers"
diff --git a/docs/translations/api-docs/data-grid/data-grid-pt.json b/docs/translations/api-docs/data-grid/data-grid-pt.json
index 845ea76bf8dc2..98edaa2f1c678 100644
--- a/docs/translations/api-docs/data-grid/data-grid-pt.json
+++ b/docs/translations/api-docs/data-grid/data-grid-pt.json
@@ -209,6 +209,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column header's title container element"
},
+ "columnHeaderTitleContainerContent": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the column header's title excepted buttons"
+ },
"columnHeaders": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column headers"
diff --git a/docs/translations/api-docs/data-grid/data-grid-zh.json b/docs/translations/api-docs/data-grid/data-grid-zh.json
index 845ea76bf8dc2..98edaa2f1c678 100644
--- a/docs/translations/api-docs/data-grid/data-grid-zh.json
+++ b/docs/translations/api-docs/data-grid/data-grid-zh.json
@@ -209,6 +209,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column header's title container element"
},
+ "columnHeaderTitleContainerContent": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the column header's title excepted buttons"
+ },
"columnHeaders": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column headers"
diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json
index 845ea76bf8dc2..98edaa2f1c678 100644
--- a/docs/translations/api-docs/data-grid/data-grid.json
+++ b/docs/translations/api-docs/data-grid/data-grid.json
@@ -209,6 +209,10 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column header's title container element"
},
+ "columnHeaderTitleContainerContent": {
+ "description": "Styles applied to {{nodeName}}.",
+ "nodeName": "the column header's title excepted buttons"
+ },
"columnHeaders": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the column headers"
diff --git a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx
index 40378758b9cb4..12dfee4184fec 100644
--- a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx
+++ b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx
@@ -64,6 +64,7 @@ const useUtilityClasses = (ownerState: OwnerState) => {
],
draggableContainer: ['columnHeaderDraggableContainer'],
titleContainer: ['columnHeaderTitleContainer'],
+ titleContainerContent: ['columnHeaderTitleContainerContent'],
};
return composeClasses(slots, getDataGridUtilityClass, classes);
@@ -232,13 +233,15 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) {
{...draggableEventHandlers}
>
- {headerComponent || (
-
- )}
+
+ {headerComponent || (
+
+ )}
+
{columnTitleIconButtons}
diff --git a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts
index 0f4e196f09c4f..a1f15db348342 100644
--- a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts
+++ b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts
@@ -121,6 +121,11 @@ export const GridRootStyles = styled('div', {
whiteSpace: 'nowrap',
overflow: 'hidden',
},
+ [`& .${gridClasses.columnHeaderTitleContainerContent}`]: {
+ overflow: 'hidden',
+ display: 'flex',
+ alignItems: 'center',
+ },
[`& .${gridClasses.sortIcon}, & .${gridClasses.filterIcon}`]: {
fontSize: 'inherit',
},
diff --git a/packages/grid/x-data-grid/src/constants/gridClasses.ts b/packages/grid/x-data-grid/src/constants/gridClasses.ts
index 659dcafc5b4a4..a1f370d9cf985 100644
--- a/packages/grid/x-data-grid/src/constants/gridClasses.ts
+++ b/packages/grid/x-data-grid/src/constants/gridClasses.ts
@@ -105,6 +105,10 @@ export interface GridClasses {
* Styles applied to the column header's title container element.
*/
columnHeaderTitleContainer: string;
+ /**
+ * Styles applied to the column header's title excepted buttons.
+ */
+ columnHeaderTitleContainerContent: string;
/**
* Styles applied to the column headers.
*/
@@ -394,6 +398,7 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [
'columnHeaderDropZone',
'columnHeaderTitle',
'columnHeaderTitleContainer',
+ 'columnHeaderTitleContainerContent',
'columnHeaders',
'columnHeadersInner',
'columnHeadersInner--scrollable',
diff --git a/packages/grid/x-data-grid/src/hooks/features/keyboard/useGridKeyboardNavigation.ts b/packages/grid/x-data-grid/src/hooks/features/keyboard/useGridKeyboardNavigation.ts
index b35b342d62d89..f619849ebaf8c 100644
--- a/packages/grid/x-data-grid/src/hooks/features/keyboard/useGridKeyboardNavigation.ts
+++ b/packages/grid/x-data-grid/src/hooks/features/keyboard/useGridKeyboardNavigation.ts
@@ -8,6 +8,8 @@ import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler';
import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
import { gridVisibleSortedRowEntriesSelector } from '../filter/gridFilterSelector';
import { useCurrentPageRows } from '../../utils/useCurrentPageRows';
+import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../../../colDef/gridCheckboxSelectionColDef';
+import { gridClasses } from '../../../constants/gridClasses';
/**
* @requires useGridPage (state)
@@ -166,12 +168,21 @@ export const useGridKeyboardNavigation = (
);
const handleColumnHeaderKeyDown = React.useCallback<
- GridEventListener
+ GridEventListener
>(
(params, event) => {
- if (!params.field) {
+ const headerTitleNode = event.currentTarget.querySelector(
+ `.${gridClasses.columnHeaderTitleContainerContent}`,
+ );
+ const isFromInsideContent =
+ !!headerTitleNode && headerTitleNode.contains(event.target as Node | null);
+
+ if (isFromInsideContent && params.field !== GRID_CHECKBOX_SELECTION_COL_DEF.field) {
+ // When focus is on a nested input, keyboard events have no effect to avoid conflicts with native events.
+ // There is one exception for the checkBoxHeader
return;
}
+
const dimensions = apiRef.current.getRootDimensions();
if (!dimensions) {
return;
diff --git a/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx
index 9e723697b7f74..844bb87d079b4 100644
--- a/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx
+++ b/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx
@@ -1,5 +1,5 @@
import * as React from 'react';
-import { createRenderer, fireEvent, screen, createEvent } from '@mui/monorepo/test/utils';
+import { createRenderer, fireEvent, screen } from '@mui/monorepo/test/utils';
import Portal from '@mui/material/Portal';
import { spy } from 'sinon';
import { expect } from 'chai';
@@ -365,42 +365,97 @@ describe(' - Keyboard', () => {
fireEvent.keyDown(document.activeElement!, { key: 'PageDown' });
expect(getActiveCell()).to.equal(`5-1`);
});
- });
- /* eslint-enable material-ui/disallow-active-element-as-key-event-target */
- it('should be able to type in an child input', () => {
- const handleInputKeyDown = spy((event) => event.defaultPrevented);
+ it('should move focus when the focus is on a column header button', function test() {
+ if (isJSDOM) {
+ // This test is not relevant if we can't choose the actual height
+ this.skip();
+ }
- const columns = [
- {
- field: 'name',
- headerName: 'Name',
- width: 200,
- renderCell: () => (
-
- ),
- },
- ];
+ render( );
- const rows = [
- {
- id: 1,
- name: 'John',
- },
- ];
+ // get the sort button in column header 1
+ const columnMenuButton = getColumnHeaderCell(1).querySelector(
+ `button[title="Sort"]`,
+ ) as HTMLElement;
- render(
-
-
-
,
- );
- const input = screen.getByTestId('custom-input');
- fireClickEvent(input);
- const keydownEvent = createEvent.keyDown(input, {
- key: 'a',
+ // Simulate click on this button
+ fireEvent.mouseUp(columnMenuButton);
+ fireEvent.click(columnMenuButton);
+ columnMenuButton.focus();
+
+ fireEvent.keyDown(document.activeElement!, { key: 'ArrowDown' });
+ expect(getActiveCell()).to.equal(`0-1`);
+ });
+
+ /* eslint-enable material-ui/disallow-active-element-as-key-event-target */
+ it('should be able to type in an child input', () => {
+ const columns = [
+ {
+ field: 'name',
+ headerName: 'Name',
+ width: 200,
+ renderCell: () => ,
+ },
+ ];
+
+ const rows = [
+ {
+ id: 1,
+ name: 'John',
+ },
+ ];
+
+ render(
+
+
+
,
+ );
+ const input = screen.getByTestId('custom-input');
+ fireEvent.mouseUp(input);
+ fireEvent.click(input);
+ input.focus();
+
+ // This does not work with navigation keys.
+ // For now, the workaround for developers is to stop the propagation
+ // But adding input is discouraged, action column or edit mode are better options
+ expect(fireEvent.keyDown(input, { key: 'a' })).to.equal(true);
+ });
+
+ it('should be able to use keyboard in a columnHeader child input', () => {
+ const columns = [
+ {
+ field: 'name',
+ headerName: 'Name',
+ width: 200,
+ renderHeader: () => ,
+ },
+ ];
+
+ const rows = [
+ {
+ id: 1,
+ name: 'John',
+ },
+ ];
+
+ render(
+
+
+
,
+ );
+ const input = screen.getByTestId('custom-input');
+ fireEvent.mouseUp(input);
+ fireEvent.click(input);
+ input.focus();
+
+ // Verify that the event is not prevented during the bubbling.
+ // fireEvent.keyDown return false if it is the case
+ // For more info, see the related discussion: https://github.com/mui/mui-x/pull/3624#discussion_r787767632
+ expect(fireEvent.keyDown(input, { key: 'a' })).to.equal(true);
+ expect(fireEvent.keyDown(input, { key: ' ' })).to.equal(true);
+ expect(fireEvent.keyDown(input, { key: 'ArrowLeft' })).to.equal(true);
});
- fireEvent(input, keydownEvent);
- expect(handleInputKeyDown.returnValues).to.deep.equal([false]);
});
it('should ignore events coming from a portal inside the cell', () => {