diff --git a/packages/mui-material/src/InputBase/InputBase.js b/packages/mui-material/src/InputBase/InputBase.js index 36e7679ca95a4f..f860a081144fe0 100644 --- a/packages/mui-material/src/InputBase/InputBase.js +++ b/packages/mui-material/src/InputBase/InputBase.js @@ -455,14 +455,38 @@ const InputBase = React.forwardRef(function InputBase(inProps, ref) { }, []); const handleClick = (event) => { - if (inputRef.current && event.currentTarget === event.target) { - inputRef.current.focus(); - } + 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; 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')) {