@@ -824,6 +824,32 @@ describe('<ButtonBase />', () => {
824824 setProps ( { disabled : false } ) ;
825825 expect ( button ) . not . to . have . attribute ( 'aria-disabled' ) ;
826826 } ) ;
827+
828+ it ( 'should not propagate click events when Space is released on a disabled non-native button' , async ( ) => {
829+ const parentClickSpy = spy ( ) ;
830+ const buttonClickSpy = spy ( ) ;
831+
832+ const { user } = render (
833+ < div onClick = { parentClickSpy } >
834+ < ButtonBase component = "span" disabled onClick = { buttonClickSpy } >
835+ Hello
836+ </ ButtonBase >
837+ </ div > ,
838+ ) ;
839+
840+ const button = screen . getByRole ( 'button' ) ;
841+
842+ // We don't use `user.tab()` because normal tab focus won't land on a disabled
843+ // ButtonBase, only programmatic focus can happen
844+ await act ( async ( ) => {
845+ button . focus ( ) ;
846+ } ) ;
847+
848+ await user . keyboard ( ' ' ) ;
849+
850+ expect ( buttonClickSpy . callCount ) . to . equal ( 0 ) ;
851+ expect ( parentClickSpy . callCount ) . to . equal ( 0 ) ;
852+ } ) ;
827853 } ) ;
828854
829855 describe ( 'prop: component' , ( ) => {
@@ -1182,6 +1208,37 @@ describe('<ButtonBase />', () => {
11821208 expect ( onClickSpy . callCount ) . to . equal ( 0 ) ;
11831209 } ) ;
11841210
1211+ it ( 'should preserve native button keyboard behavior when a custom component renders a native button' , async ( ) => {
1212+ const onClickSpy = spy ( ) ;
1213+ const onKeyDownSpy = spy ( ) ;
1214+
1215+ /** @type {React.ForwardRefExoticComponent<React.ButtonHTMLAttributes<HTMLButtonElement>> } */
1216+ const MyButton = React . forwardRef ( ( props , ref ) => < button ref = { ref } { ...props } /> ) ;
1217+
1218+ const { user } = render (
1219+ < ButtonBase component = { MyButton } onClick = { onClickSpy } onKeyDown = { onKeyDownSpy } >
1220+ Hello
1221+ </ ButtonBase > ,
1222+ ) ;
1223+
1224+ await user . tab ( ) ;
1225+
1226+ await user . keyboard ( '{Enter}' ) ;
1227+
1228+ expect ( onKeyDownSpy . callCount ) . to . equal ( 1 ) ;
1229+ expect ( onClickSpy . callCount ) . to . equal ( 1 ) ;
1230+ expect ( onKeyDownSpy . firstCall . args [ 0 ] ) . to . have . property ( 'defaultPrevented' , false ) ;
1231+
1232+ onClickSpy . resetHistory ( ) ;
1233+ onKeyDownSpy . resetHistory ( ) ;
1234+
1235+ await user . keyboard ( ' ' ) ;
1236+
1237+ expect ( onKeyDownSpy . callCount ) . to . equal ( 1 ) ;
1238+ expect ( onClickSpy . callCount ) . to . equal ( 1 ) ;
1239+ expect ( onKeyDownSpy . firstCall . args [ 0 ] ) . to . have . property ( 'defaultPrevented' , false ) ;
1240+ } ) ;
1241+
11851242 it ( 'prevents default on Enter with an anchor and empty href' , async ( ) => {
11861243 const onClickSpy = spy ( ) ;
11871244 const onKeyDownSpy = spy ( ) ;
0 commit comments