From 34924cfea7c6efdd7b1bd25a04c2d1923b43f30d Mon Sep 17 00:00:00 2001 From: Shalini Saloni Date: Tue, 18 Nov 2025 21:22:50 +0530 Subject: [PATCH 1/6] fix(InputBase): restore caret position when clicking wrapper --- .../mui-material/src/InputBase/InputBase.js | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/mui-material/src/InputBase/InputBase.js b/packages/mui-material/src/InputBase/InputBase.js index 36e7679ca95a4f..6defc0b299c3aa 100644 --- a/packages/mui-material/src/InputBase/InputBase.js +++ b/packages/mui-material/src/InputBase/InputBase.js @@ -455,14 +455,35 @@ const InputBase = React.forwardRef(function InputBase(inProps, ref) { }, []); const handleClick = (event) => { - if (inputRef.current && event.currentTarget === event.target) { - inputRef.current.focus(); - } + if (inputRef.current && event.currentTarget === event.target) { + const input = inputRef.current; + + // Save current caret position (if any) + const pos = typeof input.selectionStart === 'number' + ? input.selectionStart + : input.value.length; + + input.focus(); + + // Restore caret position after focus is forced + requestAnimationFrame(() => { + try { + // If caret was at 0 due to focus forwarding, move to end instead + const restorePos = + pos === 0 && event.target === event.currentTarget + ? input.value.length + : pos; + + input.setSelectionRange(restorePos, restorePos); + } catch (e) {} + }); + } + + if (onClick) { + onClick(event); + } +}; - if (onClick) { - onClick(event); - } - }; let InputComponent = inputComponent; let inputProps = inputPropsProp; From 27b3919702c2c075fa81f5a6be08ecdb73340834 Mon Sep 17 00:00:00 2001 From: Shalini Saloni Date: Tue, 18 Nov 2025 21:52:37 +0530 Subject: [PATCH 2/6] fix(InputBase): restore caret position when clicking wrapper --- .../mui-material/src/InputBase/InputBase.js | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/mui-material/src/InputBase/InputBase.js b/packages/mui-material/src/InputBase/InputBase.js index 6defc0b299c3aa..860ca1de76c2c3 100644 --- a/packages/mui-material/src/InputBase/InputBase.js +++ b/packages/mui-material/src/InputBase/InputBase.js @@ -455,27 +455,31 @@ const InputBase = React.forwardRef(function InputBase(inProps, ref) { }, []); const handleClick = (event) => { - if (inputRef.current && event.currentTarget === event.target) { - const input = inputRef.current; - - // Save current caret position (if any) - const pos = typeof input.selectionStart === 'number' - ? input.selectionStart - : input.value.length; + const input = inputRef.current; + + if ( + input && + (input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) && + event.currentTarget === event.target + ) { + const pos = + typeof input.selectionStart === 'number' + ? input.selectionStart + : input.value.length; input.focus(); - // Restore caret position after focus is forced requestAnimationFrame(() => { - try { - // If caret was at 0 due to focus forwarding, move to end instead + try{ const restorePos = pos === 0 && event.target === event.currentTarget ? input.value.length : pos; input.setSelectionRange(restorePos, restorePos); - } catch (e) {} + } catch(err) { + console.error(err); + } }); } @@ -484,6 +488,7 @@ const InputBase = React.forwardRef(function InputBase(inProps, ref) { } }; + let InputComponent = inputComponent; let inputProps = inputPropsProp; From ac762b26a1569982dae825170a43b04cab8baafd Mon Sep 17 00:00:00 2001 From: Shalini Saloni Date: Tue, 18 Nov 2025 23:14:39 +0530 Subject: [PATCH 3/6] fix(InputBase): prevent caret from resetting when clicking wrapper --- .../mui-material/src/InputBase/InputBase.js | 49 ++++++++----------- packages/mui-material/tsconfig.json | 2 +- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/mui-material/src/InputBase/InputBase.js b/packages/mui-material/src/InputBase/InputBase.js index 860ca1de76c2c3..3e0f80f62f01af 100644 --- a/packages/mui-material/src/InputBase/InputBase.js +++ b/packages/mui-material/src/InputBase/InputBase.js @@ -455,39 +455,30 @@ const InputBase = React.forwardRef(function InputBase(inProps, ref) { }, []); const handleClick = (event) => { - const input = inputRef.current; - - if ( - input && - (input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) && - event.currentTarget === event.target - ) { - const pos = - typeof input.selectionStart === 'number' - ? input.selectionStart - : input.value.length; - - input.focus(); - - requestAnimationFrame(() => { - try{ + const input = inputRef.current; + + if ( + input && + (input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) && + event.currentTarget === event.target + ) { + const pos = + typeof input.selectionStart === 'number' ? input.selectionStart : input.value.length; + + input.focus(); + + requestAnimationFrame(() => { const restorePos = - pos === 0 && event.target === event.currentTarget - ? input.value.length - : pos; + pos === 0 && event.target === event.currentTarget ? input.value.length : pos; input.setSelectionRange(restorePos, restorePos); - } catch(err) { - console.error(err); - } - }); - } - - if (onClick) { - onClick(event); - } -}; + }); + } + if (onClick) { + onClick(event); + } + }; let InputComponent = inputComponent; let inputProps = inputPropsProp; diff --git a/packages/mui-material/tsconfig.json b/packages/mui-material/tsconfig.json index 2ff522dab0f21f..3d401814c738d2 100644 --- a/packages/mui-material/tsconfig.json +++ b/packages/mui-material/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "include": ["src/**/*", "test/**/*"], "compilerOptions": { - "moduleResolution": "Bundler" + "moduleResolution": "bundler" }, "exclude": ["test/typescript/moduleAugmentation", "src/types/OverridableComponentAugmentation.ts"] } From 872fb20d6384ce290c290fee05d73490188b8f3a Mon Sep 17 00:00:00 2001 From: Shalini Saloni Date: Tue, 18 Nov 2025 23:17:33 +0530 Subject: [PATCH 4/6] fix(InputBase): prevent caret from resetting when clicking wrapper --- packages/mui-material/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mui-material/tsconfig.json b/packages/mui-material/tsconfig.json index 3d401814c738d2..2ff522dab0f21f 100644 --- a/packages/mui-material/tsconfig.json +++ b/packages/mui-material/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../tsconfig.json", "include": ["src/**/*", "test/**/*"], "compilerOptions": { - "moduleResolution": "bundler" + "moduleResolution": "Bundler" }, "exclude": ["test/typescript/moduleAugmentation", "src/types/OverridableComponentAugmentation.ts"] } From 0dcf7b5e52ab74cfc2a937ecbea10472b604baab Mon Sep 17 00:00:00 2001 From: Shalini Saloni Date: Wed, 19 Nov 2025 11:31:30 +0530 Subject: [PATCH 5/6] [material][InputBase] Fix caret position reset when clicking on wrapper --- .../mui-material/src/InputBase/InputBase.js | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/packages/mui-material/src/InputBase/InputBase.js b/packages/mui-material/src/InputBase/InputBase.js index 3e0f80f62f01af..f860a081144fe0 100644 --- a/packages/mui-material/src/InputBase/InputBase.js +++ b/packages/mui-material/src/InputBase/InputBase.js @@ -455,30 +455,37 @@ const InputBase = React.forwardRef(function InputBase(inProps, ref) { }, []); const handleClick = (event) => { - const input = inputRef.current; - - if ( - input && - (input instanceof HTMLInputElement || input instanceof HTMLTextAreaElement) && - event.currentTarget === event.target - ) { - const pos = - typeof input.selectionStart === 'number' ? input.selectionStart : input.value.length; - - input.focus(); - - requestAnimationFrame(() => { - const restorePos = - pos === 0 && event.target === event.currentTarget ? input.value.length : pos; - + const input = inputRef.current; + + if ( + input && + event.currentTarget === event.target && + (input.nodeName === 'INPUT' || input.nodeName === 'TEXTAREA') + ) { + const pos = + typeof input.selectionStart === 'number' + ? input.selectionStart + : input.value.length; + + input.focus(); + + requestAnimationFrame(() => { + const restorePos = + pos === 0 && event.target === event.currentTarget + ? input.value.length + : pos; + + if (typeof input.setSelectionRange === 'function') { input.setSelectionRange(restorePos, restorePos); - }); - } + } + }); + } + + if (onClick) { + onClick(event); + } +}; - if (onClick) { - onClick(event); - } - }; let InputComponent = inputComponent; let inputProps = inputPropsProp; From bfd6104205a27e56eaac1e20919e89c720bdd1e3 Mon Sep 17 00:00:00 2001 From: Shalini Saloni Date: Wed, 19 Nov 2025 14:14:37 +0530 Subject: [PATCH 6/6] test(InputBase): add caret position preservation test --- .../src/InputBase/InputBase.test.js | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/mui-material/src/InputBase/InputBase.test.js b/packages/mui-material/src/InputBase/InputBase.test.js index d1f047ae040c1f..45635ea1ac89e3 100644 --- a/packages/mui-material/src/InputBase/InputBase.test.js +++ b/packages/mui-material/src/InputBase/InputBase.test.js @@ -1,7 +1,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { expect } from 'chai'; -import { spy } from 'sinon'; +import sinon, { spy } from 'sinon'; import { act, createRenderer, fireEvent, screen, reactMajor } from '@mui/internal-test-utils'; import { ThemeProvider } from '@emotion/react'; import FormControl, { useFormControl } from '@mui/material/FormControl'; @@ -694,6 +694,32 @@ describe('', () => { }); }); + describe('caret behavior', () => { + it('should preserve caret position when clicking on the Root wrapper', () => { + const clock = sinon.useFakeTimers(); + + const { container } = render(); + const input = container.querySelector('input'); + + act(() => { + input.setSelectionRange(2, 2); + }); + + const wrapper = input.parentElement; + + fireEvent.click(wrapper); + + act(() => { + clock.runAll(); + }); + + expect(input.selectionStart).to.equal(2); + expect(input.selectionEnd).to.equal(2); + + clock.restore(); + }); + }); + describe('prop: focused', () => { it('should render correct border color with `ThemeProvider` imported from `@emotion/react`', function test() { if (window.navigator.userAgent.includes('jsdom')) {