Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TextField] Styling bug when controlled component uses autoFill #36448

Open
2 tasks done
dwjohnston opened this issue Mar 7, 2023 · 17 comments · May be fixed by #44135
Open
2 tasks done

[TextField] Styling bug when controlled component uses autoFill #36448

dwjohnston opened this issue Mar 7, 2023 · 17 comments · May be fixed by #44135
Assignees
Labels
bug 🐛 Something doesn't work component: text field This is the name of the generic UI component, not the React module!

Comments

@dwjohnston
Copy link
Contributor

dwjohnston commented Mar 7, 2023

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Steps to reproduce 🕹

Link to live example:

Steps:

  1. Visit: https://d4yz1f.csb.app/? using a Chrome browser
  2. Fill in the 'CREATE PASSWORD HERE' section, submit the form, and save password in your browsers password manager
    3.Reload page - observe the styling issue.

Current behavior 😯

image

Expected behavior 🤔

Should not have that overlap when autofill is present.

Here is the same app using @mui/[email protected] and the issue is not present:
https://v1kf03.csb.app/?

Context 🔦

I upgraded from 5.9.3 to 5.11.12 and now my login page looks bad.

Your environment 🌎

See code sandbox.

@dwjohnston dwjohnston added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Mar 7, 2023
@dwjohnston
Copy link
Contributor Author

I manually search through Mui versions to find that this bug appears to have been introduced between 5.10.11 and 5.10.12

@dwjohnston
Copy link
Contributor Author

I couldn't reproduce this issue on Firefox, mostly because I'm struggling with it to get the autofill to work.

@zannager zannager added the component: text field This is the name of the generic UI component, not the React module! label Mar 7, 2023
@michaldudak
Copy link
Member

Thanks for reporting this. I was able to reproduce the issue using the Material UI version you specified.

@michaldudak michaldudak added bug 🐛 Something doesn't work and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Mar 13, 2023
@michaldudak michaldudak changed the title Styling bug on TextField when controlled component and using autoFill. [TextField] Styling bug when controlled component uses autoFill Mar 13, 2023
@sivanzheng
Copy link

Thanks to this comment facebook/react#1159 (comment), I temporarily solved the problem and it may be helpful to you.

const [wasInitiallyAutofilled, setWasInitiallyAutofilled] = useState(false)

useLayoutEffect(
    () => {
        /**
         * You can access chrome://flags/#unsafely-treat-insecure-origin-as-secure in your navigation bar
         * and add your domain there for enable autofill in local development
         */
        let autofilled = false
        const checkAutofill = () => {
            if (autofilled) return
            const inputElements = document.getElementsByTagName('input')
            if (!inputElements) return
            for (let i = 0; i < inputElements.length; i++) {
                const input = inputElements[i]
                const isAutofilled = input.matches('*:-webkit-autofill')
                if (isAutofilled) {
                    autofilled = true
                    break
                }
            }
            setWasInitiallyAutofilled(autofilled)
        }
        // The time when it's ready is not very stable, so check few times
        setTimeout(checkAutofill, 500)
        setTimeout(checkAutofill, 1000)
        setTimeout(checkAutofill, 2000)
    }, 
    [],
)

<Form>
    <FormTextField
        name="email"
        focused={wasInitiallyAutofilled ? true : undefined}
        ...
    />
    <FormPassword
        name="password"
        focused={wasInitiallyAutofilled ? true : undefined}
        ...
    />
</Form>

@scarabaeus
Copy link

scarabaeus commented Aug 4, 2023

Not great, but a slightly better work-around than above, IMO:

// For re-usability, I made this a function that accepts a useState set function
// and returns a handler for each input to use, since you have at least two 
// TextFields to deal with.
const makeAnimationStartHandler = (stateSetter) => (e) => {
  const autofilled = !!e.target?.matches("*:-webkit-autofill");
  if (e.animationName === "mui-auto-fill") {
    stateSetter(autofilled);
  }

  if (e.animationName === "mui-auto-fill-cancel") {
    stateSetter(autofilled);
  }
};

...

<TextField
  type="password"
  id="password"
  inputProps={{
    onAnimationStart: makeAnimationStartHandler(setPasswordHasValue)
  }}
  InputLabelProps={{
    shrink: passwordHasValue
  }}
  label="Password"
  value={password}
  onChange={(e) => {
    setPassword(e.target.value);
    ...
  }}
/>

https://stackoverflow.com/questions/76830737/chrome-autofill-causes-textbox-collision-for-textfield-label-and-value/76833254#76833254

@ErrolFrancois
Copy link

Hi guys. I am new to this, but what I did find when I was busy with a form is that if there is a specific input that needs to be evaluated even when auto filled, and you can't find any solution yet, useEffect can help. When that specific value changes, whether filled or auto filled, it will get triggered.

@Ruffeng
Copy link

Ruffeng commented Jan 17, 2024

This is happening to me and the @scarabaeus solution did not work unfortunately, but clearly, we have this bug in MUI 5 in our project, for those who have the auto-fill activated and only access the page that already fills all available fields.

@ErrolFrancois
Copy link

ErrolFrancois commented Feb 8, 2024

Hi Ruffeng. I am not sure if you found any other solution yet... Check this out and let me know if this helps.
You can also add a useEffect, so that when the page loads, the useEffect will trigger "updateInputClass()" without any prop.
During updating your form, you can pass the input name on an 'onBlur' event. Here, i am using react hook forms that give access to errors and touchedFields.
But you can propbably tweek it for your use.

` function itterateInputs() {
const inputs = document.querySelectorAll("input");
inputs.forEach((input) => {
input.value.length > 0
? input.classList.add("filled-input")
: input.classList.remove("filled-input");
});
}

function updateInputClass(inputName) {
const input = inputName && document.getElementsByName(inputName)[0];
if (inputName) {
if (!errors[inputName] && touchedFields[inputName]) {
input.classList.add("filled-input");
} else if (errors[inputName]) {
input.classList.remove("filled-input");
input.classList.add("error-input");
} else {
input.classList.remove("filled-input");
}
} else {
itterateInputs();
}
} `

@ErrolFrancois
Copy link

You can also remove the error part in the code that I sent. I have found that the following code has been working for me, to add the "error-input" class to the input.
This is a custom input, so just check the className:
<FormInput {...formInputs[inputName]} register={register} onChange={handleInputChange} className={ errors[inputName] ? "error-input" : !errors[inputName] && touchedFields[inputName] ? "filled-input" : "" } errorMessage={errors[inputName]?.message} />

@srikant-thrive
Copy link

Is there an ETA on this , we are facing the same issue and will be a huge timesaver for us to have this fixed.

@michaldudak
Copy link
Member

@DiegoAndai, I'm assigning this to you so you can prioritize it.

@DiegoAndai
Copy link
Member

DiegoAndai commented Jul 17, 2024

Looked into this issue today.

Bug explanation

There are two things that together cause the behavior:

Because said effect runs after onFilled, and it reads the mismatched controlled value (which is empty), filled is incorrectly reset to false.

Solutions

There's an immediate solution which is this change:

Click to toggle change
diff --git a/packages/mui-material/src/FormControl/FormControl.js b/packages/mui-material/src/FormControl/FormControl.js
index e9d5d01b77..590e7eaf18 100644
--- a/packages/mui-material/src/FormControl/FormControl.js
+++ b/packages/mui-material/src/FormControl/FormControl.js
@@ -192,6 +192,14 @@ const FormControl = React.forwardRef(function FormControl(inProps, ref) {
     };
   }
 
+  const onFilled = React.useCallback(() => {
+    setFilled(true);
+  }, []);
+
+  const onEmpty = React.useCallback(() => {
+    setFilled(false);
+  }, []);
+
   const childContext = React.useMemo(() => {
     return {
       adornedStart,
@@ -207,12 +215,8 @@ const FormControl = React.forwardRef(function FormControl(inProps, ref) {
       onBlur: () => {
         setFocused(false);
       },
-      onEmpty: () => {
-        setFilled(false);
-      },
-      onFilled: () => {
-        setFilled(true);
-      },
+      onEmpty,
+      onFilled,
       onFocus: () => {
         setFocused(true);
       },
@@ -229,6 +233,8 @@ const FormControl = React.forwardRef(function FormControl(inProps, ref) {
     focused,
     fullWidth,
     hiddenLabel,
+    onEmpty,
+    onFilled,
     registerEffect,
     required,
     size,

But I think the correct solution will be to eventually refactor the autofill style implementation to rely on the :autofill CSS selector.

So I propose going for the temporal solution and creating an issue to implement this refactor in the future. What do you think @aarongarciah?

Other

A note on the repro

After user interaction the text field value shows as [object Object] because the onChange callback is incorrect:

 <TextField
     value={value}
-    onChange={setValue}
+    onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
+        setValue(event.target.value);
+   }}
     autoComplete="username"
     id="outlined-basic"
     label="username"
     variant="outlined"
 />

Thanks for the help

Thanks @dwjohnston for pinning the exact version on which this was introduced, it was really helpful 👌🏼.

@aarongarciah
Copy link
Member

@DiegoAndai makes sense, the temporal solution is an improvement already.

@srikant-thrive
Copy link

Any updates on this ?

@AndreyVerkhusha
Copy link

any information?

@DiegoAndai
Copy link
Member

Sorry for the delay, I'll try to get a PR for this next week.

@DiegoAndai
Copy link
Member

For the people experiencing this issue, may I ask you to test with #44135 build and check if the issue is fixed? You can do so by doing the following on your project's package.json:

"@mui/material": "https://pkg.csb.dev/mui/material-ui/commit/6b32256d/@mui/material",

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: text field This is the name of the generic UI component, not the React module!
Projects
None yet
Development

Successfully merging a pull request may close this issue.