stackworx / formik-mui

Bindings for using Formik with Material-UI

Home Page:https://stackworx.github.io/formik-mui

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

lunule opened this issue · 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: 'gabor.lippert@gmail.com', 
      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.