Repository: mui/material-ui (TypeScript)
Date: 2021-03-12 22:03:34+00:00
Task type: Bug Fix

Issue:
[Button] Submit is always triggered v5
## Current Behavior 😯

In version 5 a button click always triggers a submit.

## Expected Behavior 🤔

Submit should only be triggered if the button is of type 'submit'.

## Steps to Reproduce 🕹

Version 5:
https://codesandbox.io/s/funny-silence-4wdsx?file=/src/App.tsx

Version 4:
https://codesandbox.io/s/cool-forest-25i0x?file=/src/App.tsx



Issue discussion:
I can confirm the change of behavior between v4 and v5. This is either a regression or a breaking change.

This touch on something I have been wondering for quite some time. The default type of a `<button>` element is "submit", not "button" like Material-UI do. What would be more intuitive for developers in v5? cc @eps1lon.

I haven't benchmark what the other UI libraries are doing yet (to get a sense of what could be considered intuitive). I didn't know that IE7 used to have "button" as the default https://stackoverflow.com/questions/31644723/what-is-the-default-button-type/31644856. 

As far as I recall, we changed the default for two reasons: 1. It forces developers to be explicit about the cases when type="submit" is intended. 2. It avoid random buttons to submit an incomplete form, where React makes it easier to have complex form.

I would be leaning toward labelling this as a regression to keep a behavior that 1. seems a better default, 2. to avoid yet another breaking change 3. I can't recall any complaints about it so far. 

If somebody want to make a case for the opposite, it would also be interesting to discuss. 
I would follow the rationale of [`react/button-has-type`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/button-has-type.md) and let our `Button` be a `button[type="button"]` by default i.e. consider the v5 behavior a bug.

Edit:
Some anecdotal evidence that supports the "submit buttons are usually undesired" rationale: 
- https://github.com/facebook/react/issues/20937
It seems to have been broken in #22883. At least, it was between these two versions [v5.0.0-alpha.11...v5.0.0-alpha.12](https://github.com/mui-org/material-ui/compare/v5.0.0-alpha.11...v5.0.0-alpha.12).
I could get the correct behavior with:

```diff
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
index 75e43ed0d1..977edc3b29 100644
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -102,6 +102,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {
     onDragLeave,
     tabIndex = 0,
     TouchRippleProps,
+    type,
     ...other
   } = props;

@@ -292,7 +293,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {

   const buttonProps = {};
   if (ComponentProp === 'button') {
-    buttonProps.type = other.type === undefined ? 'button' : other.type;
+    buttonProps.type = type === undefined ? 'button' : type;
     buttonProps.disabled = disabled;
   } else {
     if (ComponentProp !== 'a' || !other.href) {
@@ -361,6 +362,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {
       onTouchStart={handleTouchStart}
       ref={handleRef}
       tabIndex={disabled ? -1 : tabIndex}
+      type={type}
       {...buttonProps}
       {...other}
     >
```

as well as all the other tests green. I had to test 2 other approaches before landing on this one (saved by the existing tests). We missed a test case for this default type.
@MMMMDCCXI Would you like to work on a pull request for this problem :)? We would need an extra test case. 
> @MMMMDCCXI Would you like to work on a pull request for this problem :)? We would need an extra test case.

My skills are not enough for this yet. I just started a few days ago with front-end development in JS / React.

I come from .NET desktop world and just taking first steps in web development. 🙂 
@oliviertassinari I'm interested in giving it a shot as my first PR. If you'd like me to use your solution and just add a test for it let me know.
@RTEYL I would suggest starting with adding a test case, then looking for a solution. The one I have proposed is good enough, I believe, but they might be better ones. The tests should give you confidence when iterating. 

Solution patch:
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -89,6 +89,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {
     onBlur,
     onClick,
     onContextMenu,
+    onDragLeave,
     onFocus,
     onFocusVisible,
     onKeyDown,
@@ -99,9 +100,9 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {
     onTouchEnd,
     onTouchMove,
     onTouchStart,
-    onDragLeave,
     tabIndex = 0,
     TouchRippleProps,
+    type,
     ...other
   } = props;
 
@@ -292,7 +293,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {
 
   const buttonProps = {};
   if (ComponentProp === 'button') {
-    buttonProps.type = other.type === undefined ? 'button' : other.type;
+    buttonProps.type = type === undefined ? 'button' : type;
     buttonProps.disabled = disabled;
   } else {
     if (ComponentProp !== 'a' || !other.href) {
@@ -361,6 +362,7 @@ const ButtonBase = React.forwardRef(function ButtonBase(inProps, ref) {
       onTouchStart={handleTouchStart}
       ref={handleRef}
       tabIndex={disabled ? -1 : tabIndex}
+      type={type}
       {...buttonProps}
       {...other}
     >


Test patch:
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.test.js b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
--- a/packages/material-ui/src/ButtonBase/ButtonBase.test.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
@@ -51,6 +51,14 @@ describe('<ButtonBase />', () => {
   }));
 
   describe('root node', () => {
+    it('should have default button type "button"', () => {
+      const { getByText, setProps } = render(<ButtonBase>Hello</ButtonBase>);
+      expect(getByText('Hello')).to.have.attribute('type', 'button');
+
+      setProps({ type: undefined });
+      expect(getByText('Hello')).to.have.attribute('type', 'button');
+    });
+
     it('should change the button type', () => {
       const { getByText } = render(<ButtonBase type="submit">Hello</ButtonBase>);
       expect(getByText('Hello')).to.have.attribute('type', 'submit');