diff --git a/packages/mui-material/src/ButtonBase/ButtonBase.js b/packages/mui-material/src/ButtonBase/ButtonBase.js index bf5f9c0c3a9a91..39fd531b63daf5 100644 --- a/packages/mui-material/src/ButtonBase/ButtonBase.js +++ b/packages/mui-material/src/ButtonBase/ButtonBase.js @@ -192,7 +192,16 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) { const isNonNativeButton = () => { const button = buttonRef.current; - return component && component !== 'button' && !(button.tagName === 'A' && button.href); + + if (!button) { + return component && component !== 'button'; + } + + if (button.tagName === 'BUTTON') { + return false; + } + + return !(button.tagName === 'A' && button.href); }; const handleKeyDown = useEventCallback((event) => { @@ -240,7 +249,8 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) { event.target === event.currentTarget && isNonNativeButton() && event.key === ' ' && - !event.defaultPrevented + !event.defaultPrevented && + !disabled ) { event.currentTarget.click(); } diff --git a/packages/mui-material/src/ButtonBase/ButtonBase.test.js b/packages/mui-material/src/ButtonBase/ButtonBase.test.js index 020dc9a3106815..c4e6322afcd57e 100644 --- a/packages/mui-material/src/ButtonBase/ButtonBase.test.js +++ b/packages/mui-material/src/ButtonBase/ButtonBase.test.js @@ -824,6 +824,32 @@ describe('', () => { setProps({ disabled: false }); expect(button).not.to.have.attribute('aria-disabled'); }); + + it('should not propagate click events when Space is released on a disabled non-native button', async () => { + const parentClickSpy = spy(); + const buttonClickSpy = spy(); + + const { user } = render( +
+ + Hello + +
, + ); + + const button = screen.getByRole('button'); + + // We don't use `user.tab()` because normal tab focus won't land on a disabled + // ButtonBase, only programmatic focus can happen + await act(async () => { + button.focus(); + }); + + await user.keyboard(' '); + + expect(buttonClickSpy.callCount).to.equal(0); + expect(parentClickSpy.callCount).to.equal(0); + }); }); describe('prop: component', () => { @@ -1182,6 +1208,37 @@ describe('', () => { expect(onClickSpy.callCount).to.equal(0); }); + it('should preserve native button keyboard behavior when a custom component renders a native button', async () => { + const onClickSpy = spy(); + const onKeyDownSpy = spy(); + + /** @type {React.ForwardRefExoticComponent>} */ + const MyButton = React.forwardRef((props, ref) =>