formik-mui icon indicating copy to clipboard operation
formik-mui copied to clipboard

<Field /> component with type {CheckboxWithLabel} kills Chrome's autofill functionality

Open lunule opened this issue 3 years ago • 0 comments

Current Behavior 😯

If a Field with type {CheckboxWithLabel} is used, it kills Chrome's autofill functionality. Fields before the checkbox lose autofill. Actually, I'm not sure if this is a MUI, a formik-mui or a Formik bug, but, as I tested it mostly with the Field component and with a formik-mui component type, I submit the bug report here.

Everything I use is the of latest version: formik-mui: 4.0.0-alpha.2, MUI 5.2.1, Formik 2.2.9.

Expected Behavior 🤔

Well, obviously, the expected behaviour is components not killing a must-have functionality of the most popular browser. :)

Steps to Reproduce 🕹

All I can add here is the code I actually have:

import logo from './logo.svg';
import './App.css';
import React, {useState} from 'react';
import { Field, Form, Formik, FormikConfig, FormikValues, useFormik } from 'formik';
import { mixed, number, object } from 'yup';

import { Box, Button, Card, CardContent, CircularProgress, Grid, Step, StepLabel, Stepper, Typography, Slider } from '@mui/material';
import { spacing } from '@mui/system';
import { CheckboxWithLabel, TextField, Checkbox } from 'formik-mui';

import { experimentalStyled as styled, createTheme, ThemeProvider } from "@mui/material/styles";
import { blue, grey } from '@mui/material/colors';
import CssBaseline from '@mui/material/CssBaseline';

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const CustomizedSlider = styled(Slider)`
  color: #20b2aa;

  transition: all .2s ease-in-out;

  :hover {
    color: #2e8b57;
  }
`;

const theme = createTheme({
  spacing: 8,
  palette: {
    primary: blue,
    background: {
      paper: '#f6f6f6',
    },    
  },  
})

const lightGrey = grey[100];

export default function App() {

  const formik = useFormik({
    initialValues: {
      kmpFieldEmail: '[email protected]', 
      kmpFieldName: '', 
      kmpFieldNewsletter: true, 
    },
    onSubmit: async values => {

      await sleep(2000);
      alert(JSON.stringify(values, null, 2));

    },
  });

    return (
      <ThemeProvider theme={theme}>
        <Card sx={{bgcolor: 'background.paper'}}>
            <CardContent>
              <FormikStepper
                initialValues={formik.values}
                onSubmit={formik.handleSubmit}
              >
                  <FormikStep label="Personal Details">
                
              <Box sx={{pb:2}}>
                <Field 
                  fullwidth 
                  type="text"
                  name="kmpFieldName" 
                  component={TextField} 
                  label="Name" 
                  inputProps={{ onChange:formik.handleChange }}
                  value={formik.values.kmpFieldName}
                  placeholder=""
                />
              </Box>

              <Box sx={{pb:2}}>
                <Field 
                  fullwidth 
                  type="email"
                  name="kmpFieldEmail" 
                  component={TextField} 
                  label="Email" 
                  inputProps={{ onChange:formik.handleChange }}
                  value={formik.values.kmpFieldEmail}
                  placeholder=""
                />
              </Box>

       <label htmlFor="email">Email Address</label>
       <input
         id="email"
         name="email"
         type="text"
         style={{display:'none'}}
       />              

              <Box sx={{pb:2}}>
                <Field  
                  type="checkbox"
                  name="kmpFieldNewsletter" 
                  component={CheckboxWithLabel} 
                  Label={{ label: 'Yes, I want to subscribe' }} 
                  inputProps={{ onChange:formik.handleChange }}
                  checked={formik.values.kmpFieldNewsletter == true ? true : false}
                />
              </Box>                

                  </FormikStep>
              
                  <FormikStep
                    label="Calorie Information"
                    validationSchema={object({
                        money: mixed().when('millionaire', {
                          is: true,
                          then: number()
                            .required()
                            .min(
                              1_000_000,
                              'Because you said you are a millionaire you need to have 1 million'
                            ),
                          otherwise: number().required(),
                        }),
                    })}
                  >

              <Box sx={{pb:2}}>
                
                    <Typography id="kmp-label--calorie-slider" gutterBottom>
                      Select how many calories you’d like your interactive sliding meal plan to follow:
                    </Typography>               

                <CustomizedSlider
                    aria-label="Calorie Target"
                    defaultValue={1800}
                    valueLabelDisplay="auto"
                    step={100}
                    marks
                    min={1200}
                    max={2500}
                />

                    <Typography id="kmp-sublabel--calorie-slider" gutterBottom>
                  Need help? Visit our <a href="/calculator/" target="_blank">Keto Calculator</a>.
                </Typography>

              </Box>

                  </FormikStep>

                  <FormikStep
                    label="Personal Preferences"
                  >

              <Box sx={{pb:2}}>
                
                <Field 
                  fullwidth 
                  type="email"
                  name="kmpFieldEmail" 
                  component={TextField} 
                  label="Email" 
                  //inputProps={{ onChange:props.handleChange }}
                  placeholder=""
                />

              </Box>

                  </FormikStep>                 

              </FormikStepper>

          </CardContent>
      </Card>
    </ThemeProvider>
  );
}

export interface FormikStepProps
  extends Pick<FormikConfig<FormikValues>, 'children' | 'validationSchema'> {
  label: string;
}

export function FormikStep({ children }: FormikStepProps) {
  return <>{children}</>;
}

export function FormikStepper({ children, ...props }: FormikConfig<FormikValues>) {
  const childrenArray = React.Children.toArray(children) as React.ReactElement<FormikStepProps>[];
  const [step, setStep] = useState(0);
  const currentChild = childrenArray[step];
  const [completed, setCompleted] = useState(false);

  const alertShit = function() {
    alert('Shit');
  }

  function isLastStep() {
    return step === childrenArray.length - 1;
  }

  return (
    <Formik
      {...props}
      validationSchema={currentChild.props.validationSchema}
      onSubmit={async (values, helpers) => {
        if (isLastStep()) {
          await props.onSubmit(values, helpers);
          setCompleted(true);
        } else {
          setStep((s) => s + 1);

          // If you have multiple fields on the same step
          // we will see they show the validation error all at the same time after the first step!
          //
          // If you want to keep that behaviour, then, comment the next line :)
          // If you want the second/third/fourth/etc steps with the same behaviour
          //    as the first step regarding validation errors, then the next line is for you! =)
          helpers.setTouched({});
        }
      }}
    >
      {({ isSubmitting }) => (
        <Form>

          <Stepper 
            alternativeLabel 
            activeStep={step}
            sx={{ mb: 4 }}
          >
        
            {childrenArray.map((child, index) => (
              <Step key={child.props.label} completed={step > index || completed}>
                <StepLabel>{child.props.label}</StepLabel>
              </Step>
            ))}
        
          </Stepper>

          {currentChild}

          <Grid 
            container 
            sx={{mt:4}} 
            justifyContent="space-between"
          >
            {step > 0 ? (
              <Grid item>
                <Button
                  disabled={isSubmitting}
                  variant="contained"
                  color="primary"
                  onClick={() => setStep((s) => s - 1)}
                >
                  Back
                </Button>
              </Grid>
            ) : null}
            <Grid item>
              <Button
                startIcon={isSubmitting ? <CircularProgress size="1rem" /> : null}
                disabled={isSubmitting}
                variant="contained"
                color="primary"
                type="submit"
              >
                {isSubmitting ? 'Submitting' : isLastStep() ? 'Submit' : 'Next'}
              </Button>
            </Grid>
          </Grid>
        </Form>
      )}
    </Formik>
  );
}

This code works - but if you remove the fixing snippet:

 <input
   id="email"
   name="email"
   type="text"
   style={{display:'none'}}
 />      

... then Chrome's autofill doesn't work anymore.

FYKI: the file is typescript (with .tsx extension) - but it doesn't matter. I tested the issue without the stuff written in typescript and the file rebaptised to .js, and the result is the same.

Thanks for investigating this further.

lunule avatar Nov 27 '21 13:11 lunule