diff --git a/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.new.test.tsx b/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.new.test.tsx
index 0ea7e598ee9d..13ab67199058 100644
--- a/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.new.test.tsx
+++ b/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.new.test.tsx
@@ -908,7 +908,7 @@ describe(' - Cell Editing', () => {
expect(spiedStartCellEditMode.lastCall.args[0]).to.deep.equal({
id: 0,
field: 'currencyPair',
- deleteValue: true,
+ initialValue: 'a',
});
});
diff --git a/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.new.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.new.test.tsx
index b382e0020ab1..602a71c5c7d5 100644
--- a/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.new.test.tsx
+++ b/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.new.test.tsx
@@ -900,7 +900,7 @@ describe(' - Row Editing', () => {
expect(spiedStartRowEditMode.lastCall.args[0]).to.deep.equal({
id: 0,
fieldToFocus: 'currencyPair',
- deleteValue: true,
+ initialValue: 'a',
});
});
diff --git a/packages/grid/x-data-grid/src/hooks/features/editRows/useGridCellEditing.new.ts b/packages/grid/x-data-grid/src/hooks/features/editRows/useGridCellEditing.new.ts
index da67e8324ef9..a255b7d5b54e 100644
--- a/packages/grid/x-data-grid/src/hooks/features/editRows/useGridCellEditing.new.ts
+++ b/packages/grid/x-data-grid/src/hooks/features/editRows/useGridCellEditing.new.ts
@@ -174,7 +174,7 @@ export const useGridCellEditing = (
}
if (reason) {
- const newParams: GridCellEditStartParams = { ...params, reason };
+ const newParams: GridCellEditStartParams = { ...params, reason, key: event.key };
apiRef.current.publishEvent('cellEditStart', newParams, event);
}
}
@@ -184,14 +184,17 @@ export const useGridCellEditing = (
const handleCellEditStart = React.useCallback>(
(params) => {
- const { id, field, reason } = params;
+ const { id, field, reason, key } = params;
const startCellEditModeParams: GridStartCellEditModeParams = { id, field };
- if (
- reason === GridCellEditStartReasons.deleteKeyDown ||
- reason === GridCellEditStartReasons.printableKeyDown
- ) {
+ if (reason === GridCellEditStartReasons.printableKeyDown) {
+ if (React.version.startsWith('18')) {
+ startCellEditModeParams.initialValue = key; // In React 17, cleaning the input is enough
+ } else {
+ startCellEditModeParams.deleteValue = true;
+ }
+ } else if (reason === GridCellEditStartReasons.deleteKeyDown) {
startCellEditModeParams.deleteValue = true;
}
@@ -328,10 +331,15 @@ export const useGridCellEditing = (
const updateStateToStartCellEditMode = useEventCallback<[GridStartCellEditModeParams], void>(
(params) => {
- const { id, field, deleteValue } = params;
+ const { id, field, deleteValue, initialValue } = params;
+
+ let newValue = apiRef.current.getCellValue(id, field);
+ if (deleteValue || initialValue) {
+ newValue = deleteValue ? '' : initialValue;
+ }
const newProps = {
- value: deleteValue ? '' : apiRef.current.getCellValue(id, field),
+ value: newValue,
error: false,
isProcessingProps: false,
};
diff --git a/packages/grid/x-data-grid/src/hooks/features/editRows/useGridRowEditing.new.ts b/packages/grid/x-data-grid/src/hooks/features/editRows/useGridRowEditing.new.ts
index 6d22397fdff5..e1880f392e77 100644
--- a/packages/grid/x-data-grid/src/hooks/features/editRows/useGridRowEditing.new.ts
+++ b/packages/grid/x-data-grid/src/hooks/features/editRows/useGridRowEditing.new.ts
@@ -237,7 +237,12 @@ export const useGridRowEditing = (
if (reason) {
const rowParams = apiRef.current.getRowParams(params.id);
- const newParams: GridRowEditStartParams = { ...rowParams, field: params.field, reason };
+ const newParams: GridRowEditStartParams = {
+ ...rowParams,
+ field: params.field,
+ key: event.key,
+ reason,
+ };
apiRef.current.publishEvent('rowEditStart', newParams, event);
}
}
@@ -247,14 +252,17 @@ export const useGridRowEditing = (
const handleRowEditStart = React.useCallback>(
(params) => {
- const { id, field, reason } = params;
+ const { id, field, reason, key } = params;
const startRowEditModeParams: GridStartRowEditModeParams = { id, fieldToFocus: field };
- if (
- reason === GridRowEditStartReasons.deleteKeyDown ||
- reason === GridRowEditStartReasons.printableKeyDown
- ) {
+ if (reason === GridRowEditStartReasons.printableKeyDown) {
+ if (React.version.startsWith('18')) {
+ startRowEditModeParams.initialValue = key; // In React 17, cleaning the input is enough
+ } else {
+ startRowEditModeParams.deleteValue = !!field;
+ }
+ } else if (reason === GridRowEditStartReasons.deleteKeyDown) {
startRowEditModeParams.deleteValue = !!field;
}
@@ -400,7 +408,7 @@ export const useGridRowEditing = (
const updateStateToStartRowEditMode = useEventCallback<[GridStartRowEditModeParams], void>(
(params) => {
- const { id, fieldToFocus, deleteValue } = params;
+ const { id, fieldToFocus, deleteValue, initialValue } = params;
const columnFields = gridColumnFieldsSelector(apiRef);
@@ -410,10 +418,13 @@ export const useGridRowEditing = (
return acc;
}
- const shouldDeleteValue = deleteValue && fieldToFocus === field;
+ let newValue = apiRef.current.getCellValue(id, field);
+ if (fieldToFocus === field && (deleteValue || initialValue)) {
+ newValue = deleteValue ? '' : initialValue;
+ }
acc[field] = {
- value: shouldDeleteValue ? '' : apiRef.current.getCellValue(id, field),
+ value: newValue,
error: false,
isProcessingProps: false,
};
diff --git a/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts b/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts
index d1fffdc8b75f..38a43c742ae4 100644
--- a/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts
+++ b/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts
@@ -207,6 +207,11 @@ export interface GridStartCellEditModeParams {
* If `true`, the value will be deleted before entering the edit mode.
*/
deleteValue?: boolean;
+ /**
+ * The initial value for the field.
+ * If `deleteValue` is also true, this value is not used.
+ */
+ initialValue?: any;
}
/**
@@ -249,6 +254,11 @@ export interface GridStartRowEditModeParams {
* If `true`, the value in `fieldToFocus` will be deleted before entering the edit mode.
*/
deleteValue?: boolean;
+ /**
+ * The initial value for the given `fieldToFocus`.
+ * If `deleteValue` is also true, this value is not used.
+ */
+ initialValue?: string;
}
/**
diff --git a/packages/grid/x-data-grid/src/models/params/gridEditCellParams.ts b/packages/grid/x-data-grid/src/models/params/gridEditCellParams.ts
index bdc732315599..cf97252b30b6 100644
--- a/packages/grid/x-data-grid/src/models/params/gridEditCellParams.ts
+++ b/packages/grid/x-data-grid/src/models/params/gridEditCellParams.ts
@@ -68,6 +68,10 @@ export interface GridCellEditStartParams<
* Only applied if `props.experimentalFeatures.newEditingApi: true`.
*/
reason?: GridCellEditStartReasons;
+ /**
+ * If the reason is related to a keyboard event, it contains which key was pressed.
+ */
+ key?: string;
}
enum GridCellEditStopReasons {
diff --git a/packages/grid/x-data-grid/src/models/params/gridRowParams.ts b/packages/grid/x-data-grid/src/models/params/gridRowParams.ts
index 6c55d5cc232f..db7a5b9cca8d 100644
--- a/packages/grid/x-data-grid/src/models/params/gridRowParams.ts
+++ b/packages/grid/x-data-grid/src/models/params/gridRowParams.ts
@@ -87,6 +87,10 @@ export interface GridRowEditStartParams
* Only applied if `props.experimentalFeatures.newEditingApi: true`.
*/
reason?: GridRowEditStartReasons;
+ /**
+ * If the reason is related to a keyboard event, it contains which key was pressed.
+ */
+ key?: string;
}
enum GridRowEditStopReasons {
diff --git a/test/e2e/fixtures/DataGrid/KeyboardEditInput.tsx b/test/e2e/fixtures/DataGrid/KeyboardEditInput.tsx
new file mode 100644
index 000000000000..ea4ff07e6126
--- /dev/null
+++ b/test/e2e/fixtures/DataGrid/KeyboardEditInput.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import { DataGrid } from '@mui/x-data-grid';
+
+const baselineProps = {
+ rows: [{ id: 0, brand: 'Nike' }],
+ columns: [{ field: 'brand', type: 'string', editable: true, width: 100 }],
+};
+
+export default function KeyboardEditInput() {
+ return (
+
+
+
+ );
+}
diff --git a/test/e2e/index.js b/test/e2e/index.js
index e60b6bd01177..827410c90316 100644
--- a/test/e2e/index.js
+++ b/test/e2e/index.js
@@ -1,5 +1,5 @@
import * as React from 'react';
-import * as ReactDOM from 'react-dom';
+import * as ReactDOM from 'react-dom/client';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import TestViewer from './TestViewer';
@@ -100,4 +100,4 @@ function App() {
);
}
-ReactDOM.render(, document.getElementById('react-root'));
+ReactDOM.createRoot(document.getElementById('react-root')).render();
diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts
index a7aceecce149..e4d97b96d3f9 100644
--- a/test/e2e/index.test.ts
+++ b/test/e2e/index.test.ts
@@ -94,9 +94,8 @@ describe('e2e', () => {
it('should select the first column header when pressing tab key', async () => {
await renderFixture('DataGrid/KeyboardNavigationFocus');
- expect(
- await page.evaluate(() => document.activeElement?.getAttribute('data-testid')),
- ).to.equal('initial-focus');
+ const button = page.locator('text=initial focus');
+ expect(await button.evaluate((node) => document.activeElement === node));
await page.keyboard.press('Tab');
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('brand');
@@ -105,9 +104,9 @@ describe('e2e', () => {
it('should implement the roving tabindex pattern', async () => {
await renderFixture('DataGrid/KeyboardNavigationFocus');
- expect(
- await page.evaluate(() => document.activeElement?.getAttribute('data-testid')),
- ).to.equal('initial-focus');
+ const button = page.locator('text=initial focus');
+ expect(await button.evaluate((node) => document.activeElement === node));
+
await page.keyboard.press('Tab');
await waitFor(async () => {
expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('brand');
@@ -153,11 +152,9 @@ describe('e2e', () => {
it('should display the rows', async () => {
await renderFixture('DataGrid/ConcurrentReactUpdate');
- expect(
- await page.evaluate(() =>
- Array.from(document.querySelectorAll('[role="cell"]')).map((node) => node.textContent),
- ),
- ).to.deep.equal(['1', '2']);
+ const cells = page.locator('[role="cell"]');
+ await cells.first().waitFor();
+ expect(await cells.allTextContents()).to.deep.equal(['1', '2']);
});
it('should work with a select as the edit cell', async () => {
@@ -174,9 +171,7 @@ describe('e2e', () => {
it('should reorder columns by dropping into the header', async () => {
await renderFixture('DataGrid/ColumnReorder');
- expect(
- await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!),
- ).to.equal('brandyear');
+ expect(await page.locator('[role="row"]').first().textContent()).to.equal('brandyear');
const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]');
const brandBoundingBox = await brand?.boundingBox();
const year = await page.$('[role="columnheader"][aria-colindex="2"] > [draggable]');
@@ -203,9 +198,7 @@ describe('e2e', () => {
it('should reorder columns by dropping into the body', async () => {
await renderFixture('DataGrid/ColumnReorder');
- expect(
- await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!),
- ).to.equal('brandyear');
+ expect(await page.locator('[role="row"]').first().textContent()).to.equal('brandyear');
const brand = await page.$('[role="columnheader"][aria-colindex="1"] > [draggable]');
const brandBoundingBox = await brand?.boundingBox();
const cell = await page.$('[role="row"][data-rowindex="0"] [role="cell"][data-colindex="1"]');
@@ -225,9 +218,7 @@ describe('e2e', () => {
);
await page.mouse.up();
}
- expect(
- await page.evaluate(() => document.querySelector('[role="row"]')!.textContent!),
- ).to.equal('yearbrand');
+ expect(await page.locator('[role="row"]').first().textContent()).to.equal('yearbrand');
});
it('should select one row', async () => {
@@ -262,11 +253,9 @@ describe('e2e', () => {
await renderFixture('DataGrid/KeyboardEditDate');
// Edit date column
- expect(
- await page.evaluate(
- () => document.querySelector('[role="cell"][data-field="birthday"]')!.textContent!,
- ),
- ).to.equal('2/29/1984');
+ expect(await page.locator('[role="cell"][data-field="birthday"]').textContent()).to.equal(
+ '2/29/1984',
+ );
// set 06/25/1986
await page.dblclick('[role="cell"][data-field="birthday"]');
@@ -274,17 +263,13 @@ describe('e2e', () => {
await page.keyboard.press('Enter');
- expect(
- await page.evaluate(
- () => document.querySelector('[role="cell"][data-field="birthday"]')!.textContent!,
- ),
- ).to.equal('6/25/1986');
+ expect(await page.locator('[role="cell"][data-field="birthday"]').textContent()).to.equal(
+ '6/25/1986',
+ );
// Edit dateTime column
expect(
- await page.evaluate(
- () => document.querySelector('[role="cell"][data-field="lastConnection"]')!.textContent!,
- ),
+ await page.locator('[role="cell"][data-field="lastConnection"]').textContent(),
).to.equal('2/20/2022, 6:50:00 AM');
// start editing lastConnection
@@ -296,9 +281,7 @@ describe('e2e', () => {
await page.keyboard.press('Enter');
expect(
- await page.evaluate(
- () => document.querySelector('[role="cell"][data-field="lastConnection"]')!.textContent!,
- ),
+ await page.locator('[role="cell"][data-field="lastConnection"]').textContent(),
).to.equal('1/31/2025, 4:05:00 PM');
});
@@ -312,6 +295,9 @@ describe('e2e', () => {
await sleep(100);
}
+ const button = page.locator('text=initial focus');
+ expect(await button.evaluate((node) => document.activeElement === node));
+
await page.keyboard.down('Tab');
await keyDown(); // 0
@@ -350,5 +336,20 @@ describe('e2e', () => {
await page.evaluate(() => (document.activeElement as HTMLInputElement).value),
).to.equal('0-1');
});
+
+ it('should start editing a cell when a printable char is pressed', async () => {
+ await renderFixture('DataGrid/KeyboardEditInput');
+
+ expect(await page.locator('[role="cell"][data-field="brand"]').textContent()).to.equal(
+ 'Nike',
+ );
+
+ await page.click('[role="cell"][data-field="brand"]');
+ await page.type('[role="cell"][data-field="brand"]', 'p');
+
+ expect(await page.locator('[role="cell"][data-field="brand"] input').inputValue()).to.equal(
+ 'p',
+ );
+ });
});
});