From 462de3e8b18e988128b84313332221dcdb87df5a Mon Sep 17 00:00:00 2001 From: Rotem Bar-Sela <44998514+rotembarsela@users.noreply.github.com> Date: Wed, 16 Oct 2024 12:42:33 +0300 Subject: [PATCH] [data grid][pickers][tree-view] Fix shortcut with localization keyboard (#14220) Co-authored-by: Olivier Tassinari --- .../tests/clipboard.DataGridPremium.test.tsx | 6 +- .../tests/cellEditing.DataGridPro.test.tsx | 8 +- .../src/tests/rowEditing.DataGridPro.test.tsx | 6 +- .../features/clipboard/useGridClipboard.ts | 10 +- .../rowSelection/useGridRowSelection.ts | 2 +- packages/x-data-grid/src/internals/index.ts | 2 +- .../x-data-grid/src/utils/keyboardUtils.ts | 24 +++- ...editing.SingleInputDateRangeField.test.tsx | 48 ++++++-- .../tests/editing.DateField.test.tsx | 104 +++++++++++++----- .../tests/selection.DateField.test.tsx | 32 ++++-- .../tests/editing.TimeField.test.tsx | 8 +- .../src/internals/hooks/useField/useField.ts | 2 +- .../useTreeViewKeyboardNavigation.test.tsx | 18 ++- .../useTreeViewKeyboardNavigation.ts | 9 +- .../src/useTreeItem2/useTreeItem2.test.tsx | 1 + 15 files changed, 199 insertions(+), 81 deletions(-) diff --git a/packages/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx index b0c31797af37..3b4c67048c67 100644 --- a/packages/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx @@ -184,7 +184,7 @@ describe(' - Clipboard', () => { getData: () => pasteText, }; - fireEvent.keyDown(cell, { key: 'v', code: 'KeyV', keyCode: 86, ctrlKey: true }); // Ctrl+V + fireEvent.keyDown(cell, { key: 'v', keyCode: 86, ctrlKey: true }); // Ctrl+V document.activeElement!.dispatchEvent(pasteEvent); } @@ -196,7 +196,7 @@ describe(' - Clipboard', () => { apiRef.current.subscribeEvent('cellEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'v', code: 'KeyV', keyCode: 86, [key]: true }); // Ctrl+V + fireEvent.keyDown(cell, { key: 'v', keyCode: 86, [key]: true }); // Ctrl+V expect(listener.callCount).to.equal(0); }); }); @@ -209,7 +209,7 @@ describe(' - Clipboard', () => { apiRef.current.subscribeEvent('rowEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'v', code: 'KeyV', keyCode: 86, [key]: true }); // Ctrl+V + fireEvent.keyDown(cell, { key: 'v', keyCode: 86, [key]: true }); // Ctrl+V expect(listener.callCount).to.equal(0); }); }); diff --git a/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx index 3fa98b164baa..067f511243c1 100644 --- a/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx @@ -924,7 +924,7 @@ describe(' - Cell editing', () => { apiRef.current.subscribeEvent('cellEditStart', listener); const cell = getCell(0, 0); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'a' }); // A + fireEvent.keyDown(cell, { key: 'a', keyCode: 65 }); // A expect(listener.callCount).to.equal(0); }); @@ -935,7 +935,7 @@ describe(' - Cell editing', () => { apiRef.current.subscribeEvent('cellEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'a', [key]: true }); // for example Ctrl + A, copy + fireEvent.keyDown(cell, { key: 'a', keyCode: 65, [key]: true }); // for example Ctrl + A, copy expect(listener.callCount).to.equal(0); }); }); @@ -946,7 +946,7 @@ describe(' - Cell editing', () => { apiRef.current.subscribeEvent('cellEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'a', shiftKey: true }); // Print A in uppercase + fireEvent.keyDown(cell, { key: 'a', keyCode: 65, shiftKey: true }); // Print A in uppercase expect(listener.callCount).to.equal(1); }); @@ -956,7 +956,7 @@ describe(' - Cell editing', () => { apiRef.current.subscribeEvent('cellEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'v', ctrlKey: true }); // Ctrl+V + fireEvent.keyDown(cell, { key: 'v', keyCode: 86, ctrlKey: true }); // Ctrl+V expect(listener.callCount).to.equal(1); }); diff --git a/packages/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx index 599de9fe4af4..c4e0882c54fc 100644 --- a/packages/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx @@ -936,7 +936,7 @@ describe(' - Row editing', () => { apiRef.current.subscribeEvent('rowEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'a', [key]: true }); + fireEvent.keyDown(cell, { key: 'a', keyCode: 65, [key]: true }); expect(listener.callCount).to.equal(0); }); }); @@ -947,7 +947,7 @@ describe(' - Row editing', () => { apiRef.current.subscribeEvent('rowEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'a', shiftKey: true }); + fireEvent.keyDown(cell, { key: 'a', keyCode: 65, shiftKey: true }); expect(listener.callCount).to.equal(1); }); @@ -967,7 +967,7 @@ describe(' - Row editing', () => { apiRef.current.subscribeEvent('rowEditStart', listener); const cell = getCell(0, 1); fireUserEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'v', ctrlKey: true }); + fireEvent.keyDown(cell, { key: 'v', keyCode: 86, ctrlKey: true }); expect(listener.callCount).to.equal(1); }); diff --git a/packages/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts b/packages/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts index 35e8e6081f97..274be843d9f3 100644 --- a/packages/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts +++ b/packages/x-data-grid/src/hooks/features/clipboard/useGridClipboard.ts @@ -4,6 +4,7 @@ import { useGridApiOptionHandler, useGridNativeEventListener } from '../../utils import { gridFocusCellSelector } from '../focus/gridFocusStateSelector'; import { serializeCellValue } from '../export/serializers/csvSerializer'; import type { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { isCopyShortcut } from '../../../utils/keyboardUtils'; function writeToClipboardPolyfill(data: string) { const span = document.createElement('span'); @@ -74,14 +75,7 @@ export const useGridClipboard = ( const handleCopy = React.useCallback( (event: KeyboardEvent) => { - if ( - !( - (event.ctrlKey || event.metaKey) && - event.key.toLowerCase() === 'c' && - !event.shiftKey && - !event.altKey - ) - ) { + if (!isCopyShortcut(event)) { return; } diff --git a/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts b/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts index cd5820115381..7fd6366dca12 100644 --- a/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts +++ b/packages/x-data-grid/src/hooks/features/rowSelection/useGridRowSelection.ts @@ -677,7 +677,7 @@ export const useGridRowSelection = ( return; } - if (event.key === 'a' && (event.ctrlKey || event.metaKey)) { + if (String.fromCharCode(event.keyCode) === 'A' && (event.ctrlKey || event.metaKey)) { event.preventDefault(); selectRows(apiRef.current.getAllRowIds(), true); } diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 7463a879c2f6..116388732fe4 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -157,7 +157,7 @@ export { getActiveElement, isEventTargetInPortal, } from '../utils/domUtils'; -export { isNavigationKey, isPasteShortcut } from '../utils/keyboardUtils'; +export { isNavigationKey, isPasteShortcut, isCopyShortcut } from '../utils/keyboardUtils'; export * from '../utils/utils'; export { exportAs } from '../utils/exportAs'; export * from '../utils/getPublicApiRef'; diff --git a/packages/x-data-grid/src/utils/keyboardUtils.ts b/packages/x-data-grid/src/utils/keyboardUtils.ts index c70617d75671..9c41f6ff5a52 100644 --- a/packages/x-data-grid/src/utils/keyboardUtils.ts +++ b/packages/x-data-grid/src/utils/keyboardUtils.ts @@ -45,13 +45,25 @@ export const isHideMenuKey = (key: React.KeyboardEvent['key']) => key === 'Tab' // In theory, on macOS, ctrl + v doesn't trigger a paste, so the function should return false. // However, maybe it's overkill to fix, so let's be lazy. export function isPasteShortcut(event: React.KeyboardEvent) { - if ( + return ( (event.ctrlKey || event.metaKey) && - event.key.toLowerCase() === 'v' && + // We can't use event.code === 'KeyV' as event.code assumes a QWERTY keyboard layout, + // for example, it would be another letter on a Dvorak physical keyboard. + // We can't use event.key === 'v' as event.key is not stable with key modifiers and keyboard layouts, + // for example, it would be ה on a Hebrew keyboard layout. + // https://github.com/w3c/uievents/issues/377 could be a long-term solution + String.fromCharCode(event.keyCode) === 'V' && !event.shiftKey && !event.altKey - ) { - return true; - } - return false; + ); +} + +// Checks if the keyboard event corresponds to the copy shortcut (CTRL+C or CMD+C) across different localization keyboards. +export function isCopyShortcut(event: KeyboardEvent): boolean { + return ( + (event.ctrlKey || event.metaKey) && + String.fromCharCode(event.keyCode) === 'C' && + !event.shiftKey && + !event.altKey + ); } diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx index b392a9d80984..d325d56d82af 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx @@ -22,7 +22,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); fireEvent.keyDown(view.getSectionsContainer(), { key: 'Delete' }); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY – MMMM YYYY'); @@ -40,7 +44,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.keyDown(input, { key: 'Delete' }); expectFieldValueV6(input, 'MMMM YYYY – MMMM YYYY'); @@ -60,7 +64,11 @@ describe(' - Editing', () => { expectFieldValueV7(view.getSectionsContainer(), 'January YYYY – MMMM YYYY'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); fireEvent.keyDown(view.getSectionsContainer(), { key: 'Delete' }); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY – MMMM YYYY'); @@ -83,7 +91,7 @@ describe(' - Editing', () => { expectFieldValueV6(input, 'January YYYY – MMMM YYYY'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.keyDown(input, { key: 'Delete' }); expectFieldValueV6(input, 'MMMM YYYY – MMMM YYYY'); @@ -102,7 +110,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); fireEvent.keyDown(view.getSectionsContainer(), { key: 'Delete' }); expect(onChangeV7.callCount).to.equal(0); @@ -122,7 +134,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.keyDown(input, { key: 'Delete' }); expect(onChangeV6.callCount).to.equal(0); @@ -262,7 +274,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY – MMMM YYYY'); @@ -280,7 +296,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: '' } }); expectFieldValueV6(input, 'MMMM YYYY – MMMM YYYY'); @@ -300,7 +316,11 @@ describe(' - Editing', () => { expectFieldValueV7(view.getSectionsContainer(), 'January YYYY – MMMM YYYY'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY – MMMM YYYY'); @@ -323,7 +343,7 @@ describe(' - Editing', () => { expectFieldValueV6(input, 'January YYYY – MMMM YYYY'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: '' } }); expectFieldValueV6(input, 'MMMM YYYY – MMMM YYYY'); @@ -342,7 +362,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); expect(onChangeV7.callCount).to.equal(0); @@ -362,7 +386,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: 'Delete' } }); expect(onChangeV6.callCount).to.equal(0); diff --git a/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx index b5140fdffb72..fafafc44a61e 100644 --- a/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx @@ -269,7 +269,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); fireUserEvent.keyPress(view.getSectionsContainer(), { key: 'Delete' }); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY'); @@ -287,7 +291,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireUserEvent.keyPress(input, { key: 'Delete' }); expectFieldValueV6(input, 'MMMM YYYY'); @@ -307,7 +311,11 @@ describe(' - Editing', () => { expectFieldValueV7(view.getSectionsContainer(), 'January YYYY'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); fireUserEvent.keyPress(view.getSectionsContainer(), { key: 'Delete' }); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY'); @@ -330,7 +338,7 @@ describe(' - Editing', () => { expectFieldValueV6(input, 'January YYYY'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireUserEvent.keyPress(input, { key: 'Delete' }); expectFieldValueV6(input, 'MMMM YYYY'); @@ -398,7 +406,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); fireUserEvent.keyPress(view.getSectionsContainer(), { key: 'Delete' }); expect(onChangeV7.callCount).to.equal(0); @@ -418,7 +430,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireUserEvent.keyPress(input, { key: 'Delete' }); expect(onChangeV6.callCount).to.equal(0); @@ -1206,7 +1218,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY'); @@ -1224,7 +1240,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: '' } }); expectFieldValueV6(input, 'MMMM YYYY'); @@ -1242,7 +1258,11 @@ describe(' - Editing', () => { expectFieldValueV7(view.getSectionsContainer(), 'January YYYY'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); expectFieldValueV7(view.getSectionsContainer(), 'MMMM YYYY'); @@ -1261,7 +1281,7 @@ describe(' - Editing', () => { expectFieldValueV6(input, 'January YYYY'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: '' } }); expectFieldValueV6(input, 'MMMM YYYY'); @@ -1433,7 +1453,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); firePasteEventV7(view.getSectionsContainer(), '09/16/2022'); @@ -1453,7 +1477,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); firePasteEventV6(input, '09/16/2022'); @@ -1471,7 +1495,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); firePasteEventV7(view.getSectionsContainer(), '09/16/2022'); @@ -1489,7 +1517,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); firePasteEventV6(input, '09/16/2022'); @@ -1507,7 +1535,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); firePasteEventV7(view.getSectionsContainer(), 'Some invalid content'); expectFieldValueV7(view.getSectionsContainer(), 'MM/DD/YYYY'); @@ -1523,7 +1555,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); firePasteEventV6(input, 'Some invalid content'); expectFieldValueV6(input, 'MM/DD/YYYY'); @@ -1543,7 +1575,11 @@ describe(' - Editing', () => { view.selectSection('year'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); firePasteEventV7(view.getSectionsContainer(), `Escaped 2014`); expect(onChangeV7.callCount).to.equal(1); @@ -1562,7 +1598,7 @@ describe(' - Editing', () => { view.selectSection('year'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); firePasteEventV6(input, `Escaped 2014`); expect(onChangeV6.callCount).to.equal(1); @@ -1582,7 +1618,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); firePasteEventV7(view.getSectionsContainer(), '09/16/2022'); expect(onChangeV7.callCount).to.equal(0); @@ -1602,7 +1642,7 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); firePasteEventV6(input, '09/16/2022'); expect(onChangeV6.callCount).to.equal(0); @@ -1734,7 +1774,7 @@ describe(' - Editing', () => { expectFieldValueV7(view.getSectionsContainer(), '12/02/2018'); // Select all sections - fireEvent.keyDown(view.getActiveSection(1), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(1), { key: 'a', keyCode: 65, ctrlKey: true }); firePasteEventV7(view.getSectionsContainer(), '09/16/2022'); expectFieldValueV7(view.getSectionsContainer(), '09/16/2022'); @@ -1816,7 +1856,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); firePasteEventV7(view.getSectionsContainer(), '09/16/2022'); expect(onChangeV7.callCount).to.equal(0); @@ -1884,7 +1928,11 @@ describe(' - Editing', () => { }); view.selectSection('month'); - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); expectFieldValueV7(view.getSectionsContainer(), 'MM/DD/YYYY'); view.selectSection('month'); @@ -1919,7 +1967,7 @@ describe(' - Editing', () => { const input = getTextbox(); view.selectSection('month'); - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: '' } }); fireUserEvent.keyPress(input, { key: 'ArrowLeft' }); @@ -2270,7 +2318,11 @@ describe(' - Editing', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); // When all sections are selected, the value only contains the key pressed view.pressKey(null, '9'); @@ -2285,7 +2337,7 @@ describe(' - Editing', () => { const input = getTextbox(); // Select all sections - fireUserEvent.keyPress(input, { key: 'a', ctrlKey: true }); + fireUserEvent.keyPress(input, { key: 'a', keyCode: 65, ctrlKey: true }); // When all sections are selected, the value only contains the key pressed fireEvent.change(input, { target: { value: '9' } }); diff --git a/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx index e0b6a0114dd1..6ef20dec53f3 100644 --- a/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx @@ -170,7 +170,11 @@ describe(' - Selection', () => { // Test with v7 input let view = renderWithProps({ enableAccessibleFieldDOMStructure: true }); view.selectSection('month'); - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(getCleanedSelectedContent()).to.equal('MM/DD/YYYY'); view.unmount(); @@ -179,7 +183,7 @@ describe(' - Selection', () => { view = renderWithProps({ enableAccessibleFieldDOMStructure: false }); const input = getTextbox(); view.selectSection('month'); - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); expect(getCleanedSelectedContent()).to.equal('MM/DD/YYYY'); }); @@ -190,7 +194,11 @@ describe(' - Selection', () => { format: `- ${adapterToUse.formats.year}`, }); view.selectSection('year'); - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(getCleanedSelectedContent()).to.equal('- YYYY'); view.unmount(); @@ -202,7 +210,7 @@ describe(' - Selection', () => { }); const input = getTextbox(); view.selectSection('year'); - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); expect(getCleanedSelectedContent()).to.equal('- YYYY'); }); }); @@ -250,7 +258,11 @@ describe(' - Selection', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(getCleanedSelectedContent()).to.equal('MM/DD/YYYY'); fireEvent.keyDown(view.getSectionsContainer(), { key: 'ArrowRight' }); @@ -264,7 +276,7 @@ describe(' - Selection', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); expect(getCleanedSelectedContent()).to.equal('MM/DD/YYYY'); fireEvent.keyDown(input, { key: 'ArrowRight' }); @@ -315,7 +327,11 @@ describe(' - Selection', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(getCleanedSelectedContent()).to.equal('MM/DD/YYYY'); fireEvent.keyDown(view.getSectionsContainer(), { key: 'ArrowLeft' }); @@ -329,7 +345,7 @@ describe(' - Selection', () => { view.selectSection('month'); // Select all sections - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); expect(getCleanedSelectedContent()).to.equal('MM/DD/YYYY'); fireEvent.keyDown(input, { key: 'ArrowLeft' }); diff --git a/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx b/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx index 9ddc968fd060..f1c4c0811bd3 100644 --- a/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx +++ b/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx @@ -679,7 +679,11 @@ describe(' - Editing', () => { }); view.selectSection('hours'); - fireEvent.keyDown(view.getActiveSection(0), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getActiveSection(0), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); view.pressKey(null, ''); fireEvent.keyDown(view.getSectionsContainer(), { key: 'ArrowLeft' }); @@ -704,7 +708,7 @@ describe(' - Editing', () => { const input = getTextbox(); view.selectSection('hours'); - fireEvent.keyDown(input, { key: 'a', ctrlKey: true }); + fireEvent.keyDown(input, { key: 'a', keyCode: 65, ctrlKey: true }); fireEvent.change(input, { target: { value: '' } }); fireEvent.keyDown(input, { key: 'ArrowLeft' }); diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts index 2ad8363212a9..c271f2a4f42b 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts @@ -125,7 +125,7 @@ export const useField = < switch (true) { // Select all case (event.ctrlKey || event.metaKey) && - event.key.toLowerCase() === 'a' && + String.fromCharCode(event.keyCode) === 'A' && !event.shiftKey && !event.altKey: { // prevent default to make sure that the next line "select all" while updating diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx index 7dab02bcf8b2..2db89b9501b1 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx @@ -1044,7 +1044,11 @@ describeTreeView< act(() => { view.getItemRoot('1').focus(); }); - fireEvent.keyDown(view.getItemRoot('1'), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getItemRoot('1'), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(view.getSelectedTreeItems()).to.deep.equal(['1', '2', '3', '4']); }); @@ -1058,7 +1062,11 @@ describeTreeView< act(() => { view.getItemRoot('1').focus(); }); - fireEvent.keyDown(view.getItemRoot('1'), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getItemRoot('1'), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(view.getSelectedTreeItems()).to.deep.equal([]); }); @@ -1076,7 +1084,11 @@ describeTreeView< act(() => { view.getItemRoot('1').focus(); }); - fireEvent.keyDown(view.getItemRoot('1'), { key: 'a', ctrlKey: true }); + fireEvent.keyDown(view.getItemRoot('1'), { + key: 'a', + keyCode: 65, + ctrlKey: true, + }); expect(view.getSelectedTreeItems()).to.deep.equal(['1', '3', '4']); }); }); diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts index 88bad3cccb21..5b2739b6f988 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts @@ -16,7 +16,7 @@ import { import { hasPlugin } from '../../utils/plugins'; import { useTreeViewLabel } from '../useTreeViewLabel'; -function isPrintableCharacter(string: string) { +function isPrintableKey(string: string) { return !!string && string.length === 1 && !!string.match(/\S/); } @@ -256,7 +256,10 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // Multi select behavior when pressing Ctrl + a // Selects all the items - case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection: { + case String.fromCharCode(event.keyCode) === 'A' && + ctrlPressed && + params.multiSelect && + !params.disableSelection: { instance.selectAllNavigableItems(event); event.preventDefault(); break; @@ -264,7 +267,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // Type-ahead // TODO: Support typing multiple characters - case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key): { + case !ctrlPressed && !event.shiftKey && isPrintableKey(key): { const matchingItem = getFirstMatchingItem(itemId, key); if (matchingItem != null) { instance.focusItem(event, matchingItem); diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx index 8dc7de297623..51e07dcabc5a 100644 --- a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx +++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx @@ -100,6 +100,7 @@ describeTreeView<[UseTreeViewExpansionSignature, UseTreeViewIconsSignature]>( const input = view.getItemRoot('1.1').querySelector('.icon-input')!; const keydownEvent = createEvent.keyDown(input, { key: 'a', + keyCode: 65, }); const handlePreventDefault = spy();