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

Disable Chrome autofill allowing autoComplete="new-password" to be passed into the Input component properties #3500

Open
rdsedmundo opened this issue Apr 3, 2019 · 16 comments
Labels
issue/needs-review issue/reviewed Issue has recently been reviewed (mid-2020)

Comments

@rdsedmundo
Copy link

Currently, the default Input component is forcefully setting autoComplete="off" which doesn't disable Chrome's autofill functionality on latest browser versions. For achieving that, we need to pass autoComplete="new-password".

The problem is that the value is hardcoded and can't be configured unless we implement our own Input component and configure this property properly there.

It's hardcoded here:

autoComplete="off"

And it gets passed to the default Input component here:

As you can see, there's no way of configuring that property manually.

@kylehurt-rkv
Copy link

I agree that this would be a welcome change. I have been fighting with this for the last couple of days. It is a dealbreaker for our app because the autocomplete control overlays the react-select menu. We have resorted to forking the project, but it would be nice if this were supported natively.

@Rall3n
Copy link
Collaborator

Rall3n commented Apr 5, 2019

As you can see, there's no way of configuring that property manually.

@rdsedmundo Yes, there is a possibility to configure it. It is hard coded as a prop to the Input component, that is true. But this library offers in its current version the possibility to overwrite its internal components using its components framework.

You just have to overwrite the Input component and overwrite the prop being passed to the component.

import Select, { components } from 'react-select';

const Input = ({ autoComplete, ...props }) => <components.Input {...props} autoComplete="new-password" />;

<Select
    { ... }
    components={{
        Input
    }}
/>

We have resorted to forking the project, but it would be nice if this were supported natively.

@kylehurt-rkv Here is your native support, without forking the repository.

@rdsedmundo
Copy link
Author

That's a smarter solution than what I did. I knew I could just overwrite the Input, that's what I did for fixing it, but I just copied its source code originally and pasted on my codebase. I haven't thought about the possibility of just importing it from the package.

I still can see value of having it configurable though.

@kylehurt-rkv
Copy link

@Rall3n We actually tried that exact solution, but whenever the page would try to load, Chrome would get of memory errors and crash. We are supplying several other custom components into the Select component, so it may be possible that something we did on one of the other custom components was conflicting with custom input component. Or maybe we are doing something in a non-standard way.
Here is our code with the addition of the custom input component. Chrome throws an out of memory error and never loads the page. I'm guessing there is a circular reference somewhere.
`
import React from 'react';
import PropTypes from 'prop-types';
import RSelect from 'react-select';
import { withStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import NoSsr from '@material-ui/core/NoSsr';
import TextField from '@material-ui/core/TextField';
import Paper from '@material-ui/core/Paper';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl/FormControl';
import FormHelperText from '@material-ui/core/FormHelperText/FormHelperText';
import { remove } from 'react-icons-kit/fa/remove';
import { chevronDown } from 'react-icons-kit/fa/chevronDown';
import { Icon } from 'react-icons-kit';
import Tooltip from '@material-ui/core/Tooltip';
import { isErrorState } from './reduxFormHelper';

const styles = theme => ({
root: {
flexGrow: 1,
},
input: {
display: 'flex',
paddingTop: 2,
paddingBottom: 3,
},
valueContainer: {
display: 'flex',
flexWrap: 'wrap',
flex: 1,
alignItems: 'center',
},
noOptionsMessage: {
padding: ${theme.spacing.unit}px ${theme.spacing.unit * 2}px,
},
singleValue: {
fontSize: 16,
},
placeholder: {
position: 'absolute',
left: 2,
fontSize: 16,
},
paper: {
position: 'absolute',
zIndex: 1,
marginTop: 0,
paddingTop: 0,
left: 0,
right: 0,
},
});

class Select extends React.Component {
findOption() {
const { input, options } = this.props;
const value = input.value._id || input.value;
return options.find(option => option.value === value);
}

render() {
const {
classes,
theme,
fullWidth,
label,
id,
placeholder,
required,
meta: { touched, error },
options,
isReadOnly,
input,
} = this.props;

function NoOptionsMessage(props) {
  return (
    <Typography
      color="textSecondary"
      className={props.selectProps.classes.noOptionsMessage}
      {...props.innerProps}
    >
      {props.children}
    </Typography>
  );
}

function inputComponent({ inputRef, ...props }) {
  return <div ref={inputRef} {...props} />;
}

const Control = props => {
  return (
    <div className="app-input-group">
      <FormControl
        fullWidth={fullWidth}
        error={isErrorState(touched, error)}
      >
        <TextField
          error={isErrorState(touched, error)}
          label={label}
          id={id}
          required={required}
          InputProps={{
            inputComponent,
            inputProps: {
              className: props.selectProps.classes.input,
              inputRef: props.innerRef,
              children: props.children,
              ...props.innerProps,
            },
          }}
          {...props.selectProps.textFieldProps}
        />
        <FormHelperText>{touched && error}</FormHelperText>
      </FormControl>
    </div>
  );
};

function Option(props) {
  return (
    <MenuItem
      buttonRef={props.innerRef}
      selected={props.isFocused}
      component="div"
      style={{
        fontWeight: props.isSelected ? 500 : 400,
      }}
      {...props.innerProps}
    >
      {props.children}
    </MenuItem>
  );
}

function Placeholder(props) {
  return (
    <Typography
      color="textSecondary"
      className={props.selectProps.classes.placeholder}
      {...props.innerProps}
    >
      {props.children}
    </Typography>
  );
}

function SingleValue(props) {
  return (
    <Typography
      className={props.selectProps.classes.singleValue}
      {...props.innerProps}
    >
      {props.children}
    </Typography>
  );
}

function ValueContainer(props) {
  return (
    <div className={props.selectProps.classes.valueContainer}>
      {props.children}
    </div>
  );
}

function Menu(props) {
  return isReadOnly ? null : (
    <Paper
      square
      className={props.selectProps.classes.paper}
      {...props.innerProps}
    >
      {props.children}
    </Paper>
  );
}

function ClearIndicator(props) {
  const {
    innerProps: { ref, ...restInnerProps },
  } = props;
  return (
    <div
      {...restInnerProps}
      ref={ref}
      className="app-select-clear-ind-container"
    >
      <Tooltip title="Clear">
        <Icon icon={remove} className="app-select-clear-ind" />
      </Tooltip>
    </div>
  );
}

function DropdownIndicator(props) {
  return (
    <div className="app-select-dd-ind-container">
      <Icon className="app-select-dd-ind" icon={chevronDown} />
    </div>
  );
}

function IndicatorsContainer(props) {
  return isReadOnly ? null : (
    <div className="app-select-ind-container">{props.children}</div>
  );
}

function IndicatorSeparator(props) {
  return <span className="app-select-ind-sep" {...props.innerProps} />;
}

const Input = ({ autoComplete, ...props }) => (
  <components.Input {...props} autoComplete="new-password" />
);

const components = {
  Control,
  Menu,
  NoOptionsMessage,
  Option,
  Placeholder,
  SingleValue,
  ValueContainer,
  DropdownIndicator,
  ClearIndicator,
  IndicatorsContainer,
  IndicatorSeparator,
  Input,
};

const selectStyles = {
  input: base => ({
    ...base,
    color: theme.palette.text.primary,
    '& input': {
      font: 'inherit',
    },
  }),
};

return (
  <div className={classes.root}>
    <NoSsr>
      <RSelect
        isClearable
        classes={classes}
        styles={selectStyles}
        isDisabled={isReadOnly}
        textFieldProps={{
          InputLabelProps: {
            shrink: true,
          },
        }}
        options={options}
        components={components}
        value={this.findOption()}
        onChange={
          !isReadOnly
            ? option => input.onChange(option ? option.value : null)
            : null
        }
        placeholder={placeholder}
        openMenuOnClick={!isReadOnly}
      />
    </NoSsr>
  </div>
);

}
}

Select.defaultProps = {
fullWidth: true,
};

Select.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};

export default withStyles(styles, { withTheme: true })(Select);

`

@kylehurt-rkv
Copy link

image
This the debugger window Chrome throws up.

@Rall3n
Copy link
Collaborator

Rall3n commented Apr 5, 2019

@kylehurt-rkv If I´m seeing this correctly, you are creating the custom components inside your render function? This could be the source of your problem.

Creating the components inside the render causes a complete rerender, because with each call of render the variables are recreated. Try declaring the components outside the render function (I would recommend outside of the class if you can, else inside the class).

@kylehurt-rkv
Copy link

@Rall3n Turns out I was right. I WAS doing something in a non-standard way. Moving the custom components outside of the class took care of the issue I was having with Chrome crashing. Makes sense now that I know the answer. Thank you very much for your insight.

@lvl99
Copy link

lvl99 commented Jul 4, 2019

Seems like there's a good reason to have this autoComplete configurable, as based on the age and responses of #2395

@gregholst
Copy link

I have a similar problem in #4006 : I am looking for a way to make sure that the Dashlane autofill icon does not get active in my AsyncComponent which results in a crash of the Chrome browser. Dashlane support hasn't been helpful so far, so if anybody of you has an idea on how to lock Dashlane out of my AsyncComponent, i'd be happy to hear!

@bladey bladey added the issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet label Jun 3, 2020
@bladey bladey added the issue/reviewed Issue has recently been reviewed (mid-2020) label Jun 17, 2020
@ebonow
Copy link
Collaborator

ebonow commented Dec 20, 2020

This seems somewhat unnecessary given the existing component api as Rall3n has already mentioned.

Here you can already pass in an autoComplete prop to the Select and have it rendered in a custom Input component.

Note: This can be applied to any prop you want to apply to the Input

Working demo: codesandbox

const Input = (props) => {
  const { autoComplete = props.autoComplete } = props.selectProps;
  return <components.Input {...props} autoComplete={autoComplete} />;
};

const MySelect = (props) => (
   <Select components={{ Input }} autoComplete="new-password" options={options} />
);

@ebonow ebonow added issue/needs-review and removed issue/bug-unconfirmed Issues that describe a bug that hasn't been confirmed by a maintainer yet labels Dec 20, 2020
@barrychapman
Copy link

This really needs to be added

@ebonow
Copy link
Collaborator

ebonow commented Feb 11, 2021

Greetings all,

It seems that Google has perhaps changed their stance on this and per the top answer here, autocomplete=off appears to now be working as expected. Can anyone confirm if this is the case or find any documentation supporting this?

In case this isn't correct, I have filed the following comment in the relevant the Chromium bugs thread: https://bugs.chromium.org/p/chromium/issues/detail?id=587466#c591

@ebonow ebonow added the awaiting-author-response Issues or PRs waiting for more information from the author label Feb 11, 2021
@ebonow
Copy link
Collaborator

ebonow commented Mar 19, 2021

Greetings all,

It seems per the above stackoverflow post and in my own testing that newer versions of Chrome do appear to respect "autocomplete=off". If anyone is experiencing any differently, please provide a codesandbox example so we can reproduce and investigate further.

If anyone needs or wants to change this attribute on the Input, this is already possible as provided here: #3500 (comment)

As such I will be closing this issue, but happy to re-open if we can confirm that this is still an issue.

@ebonow ebonow closed this as completed Mar 19, 2021
@ebonow ebonow removed the awaiting-author-response Issues or PRs waiting for more information from the author label Mar 19, 2021
@ebonow
Copy link
Collaborator

ebonow commented Jun 8, 2021

Looked into this further and several users are still experiencing autoComplete from Chrome. I will reopen this.

@philipaarseth
Copy link

philipaarseth commented Oct 28, 2021

This is still a problem in safari and setting custom input component and autoComplete="new-password" doesn't always fix it.

It may have something to do with situational rendering

Wrapping with this works
<div className={showingMenu ? "": "hidden"}>
and hidden is display: none;

Wrapping with this does not work
{showingMenu &&

edit:
name: search seems to work better
https://stackoverflow.com/a/30873633

mikellykels added a commit to getsentry/sentry that referenced this issue Apr 19, 2022
Safari is disregarding autocomplete='off' and still showing the browser autocomplete for the select action field in the alerts creator. This is due to the first/default option being set as Email, so having an invisible character to break up the word seems to fix this. Seems to be an open issue with react-select and the other solutions mentioned here don't work for Safari JedWatson/react-select#3500.

FIXES WOR-1779
mikellykels added a commit to getsentry/sentry that referenced this issue Apr 19, 2022
Safari is disregarding autocomplete='off' and still showing the browser autocomplete for the select action field in the alerts creator. This is due to the first/default option being set as Email, so having an invisible character to break up the word seems to fix this. Seems to be an open issue with react-select and the other solutions mentioned here don't work for Safari JedWatson/react-select#3500.

FIXES WOR-1779
@OperKH
Copy link

OperKH commented Sep 14, 2022

      <Input
        {...commonProps}
        autoCapitalize="none"
        autoComplete="off"
        autoCorrect="off"
        id={id}
        innerRef={this.getInputRef}
        isDisabled={isDisabled}
        isHidden={inputIsHidden}
        onBlur={this.onInputBlur}
        onChange={this.handleInputChange}
        onFocus={this.onInputFocus}
        spellCheck="false"
        tabIndex={tabIndex}
        form={form}
        type="text"
        value={inputValue}
        {...ariaAttributes}
      />

I see that search input is hardcoded to type="text". If I change it to type="search" - it solves issue with browser autofill.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
issue/needs-review issue/reviewed Issue has recently been reviewed (mid-2020)
Projects
None yet
Development

No branches or pull requests

10 participants