formik-mui
formik-mui copied to clipboard
<Field /> component with type {CheckboxWithLabel} kills Chrome's autofill functionality
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.