How to test Material ui's DatePicker with Jest and Enzyme - reactjs

i've got a problem. I'm trying to test material ui's datePicker (https://mui.com/api/date-picker/) with Jest and Enzyme. I've searched alot but couldnt find anything that would help me... I hope that you will guide me.
Here's what i got:
DatePickerFilter.tsx
import React, {useState} from 'react';
import AdapterDateFns from '#mui/lab/AdapterDateFns';
import LocalizationProvider from '#mui/lab/LocalizationProvider';
import DatePicker from '#mui/lab/DatePicker';
import TextField from '#mui/material/TextField';
import styled from '#emotion/styled';
export const StyledDatePicker = styled(DatePicker)`
width: 320px !important;
`;
const DatePickerFilter = () => {
const [date, setDate] = useState('10/10/2021')
const handleChange = (newValue: any) => {
setDate(newValue)
};
return (
<LocalizationProvider dateAdapter={AdapterDateFns}>
<StyledDatePicker
label="Date"
views={['day']}
value={date}
onChange={handleChange}
renderInput={(params) => <TextField {...params} />}
/>
</LocalizationProvider>
);
};
MyBookings.test.tsx
export default DatePickerFilter;
import React from 'react';
import { mount } from 'enzyme';
import { Render } from '../../../utilities/TestsUtils';
import DatePickerFilter from '../../common/DatePickerFilter';
describe('MyBookings > DatePickerFilter', () => {
it('should not change date if its before today', () => {
const wrapper = mount(
<Render>
<DatePickerFilter />
</Render>
);
wrapper.find('input').simulate('change', {target: {value: '11/10/2021'}});
console.log(wrapper.find('input').debug())
wrapper.unmount();
});
});
And here's a function util that gives me access to redux's store
export const store = configureStore({
reducer: {
booking: bookingReducer,
myBookings: myBookingsSlice,
officeSpaces: officeSpacesSlice,
filters: filtersSlice
},
});
export const Render = ({ children }: any) => {
return (
<Provider store={store}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
</Provider>
);
};
I'm trying to change input's value by simulate 'change' event, but it doesnt want to change.
Here's log of wrapper.find('input').debug()
<input aria-invalid={false} aria-describedby={[undefined]} autoComplete={[undefined]}
autoFocus={false} defaultValue={[undefined]} disabled={[undefined]} id={[undefined]}
onAnimationStart={[Function: handleAutoFill]} name={[undefined]} placeholder={[undefined]}
readOnly={true} required={false} rows={[undefined]} value="10/10/2021"
onKeyDown={[Function (anonymous)]} onKeyUp={[undefined]} type="text" aria-readonly={true}
aria-label="Choose date, selected date is Oct 10, 2021" onClick={[Function: openPicker]}
className="MuiOutlinedInput-input MuiInputBase-input css-1t8l2tu-MuiInputBase-input-MuiOutlinedInput-input"
onBlur={[Function: handleBlur]} onChange={[Function: handleChange]} onFocus={[Function: handleFocus]} />

The answer to this problem is mentioned here https://github.com/mui/material-ui/issues/27038.
If you don't need the Mobile variants, you can do what I did and import the DesktopDatePicker. This way I don't reproduce the problem with triggerin a change on an input.

Related

Use React Hook Form With MUI DatePicker Component In Typescript

I'm using React Hook Form & the MUI DatePicker Component with Typescript. I have a DateField component which is a reusable component containing the DatePicker.
<DateField<ReceptionInvoice>
label="Issue Date"
control={control}
name={"invoiceDate"}
options={{
required: "This field is required",
}}
error={errors.invoiceDate}
renderInput={(props) => (
<TextField
register={register}
label={props.label}
name={"invoiceDate"}
/>
)}
/>
The TextField component the MUI textField component.
The DateField component has the following code:
import DateFnsUtils from "#date-io/date-fns";
import DatePicker, { DatePickerProps } from "#mui/lab/DatePicker";
import React, { useMemo } from "react";
import { Control, Controller, FieldValues } from "react-hook-form";
import { ReceptionInvoice } from "../models/InvoiceReception";
import { ENOFieldOverrides, ENOFieldProps } from "./EnoFieldUtils";
import ENOTooltip from "./ENOTooltip";
import { makeStyles } from "#mui/styles";
import AdapterDateFns from "#mui/lab/AdapterDateFns";
import LocalizationProvider from "#mui/lab/LocalizationProvider";
export interface ENODateFieldProps<T extends FieldValues>
extends Omit<ENOFieldProps<T>, "register">,
Omit<DatePickerProps, ENOFieldOverrides | "value" | "onChange"> {
control: Control<T, object>;
}
const useStyles = makeStyles({
fullWidth: {
width: "100%",
},
});
export default function ENODateField<T extends FieldValues>({
name,
control,
options,
error,
tooltipText,
...rest
}: ENODateFieldProps<T>) {
const classes = useStyles();
return (
<ENOTooltip text={tooltipText}>
{/* MUI Tooltip wrapper, not relavant */}
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Controller
name={name}
control={control}
render={({ field: { ref, ...fieldRest } }) => (
<DatePicker className={classes.fullWidth} {...fieldRest} {...rest} />
)}
/>
</LocalizationProvider>
</ENOTooltip>
);
}
Using this code, I get the following error:
Please help me out, looking forward to your reply!

React-testing: unable to detect DatePicker field to initiate change

I have a component which uses the react-datepicker package.
I am writing to write a unit test which will edits the dates and thereafter run some logic. However, i am unable to detect the field which for me to change using userEvent.type(). I have tried to use getByText, getByRole, getAllByText.
Form.tsx
import React, { useState } from 'react';
import DatePicker from "react-datepicker";
import { Form } from 'react-bootstrap';
import "react-datepicker/dist/react-datepicker.css";
const Form = () => {
const [data, setData] = useState({ date1: new Date(), date2: new Date() })
return (
<div>
<Form>
...some other fields
<Form.Group controlId="date1">
<Form.Label>Date1</Form.Label>
<DatePicker name='date1'selected={data.date1} onChange={(date: Date) => setData({...data, date1: date})}
</Form.Group>
<Form.Group controlId="date2">
<Form.Label>Date2</Form.Label>
<DatePicker name='date2' selected={data.date2} onChange={(date: Date) => setData({...data, date2: date})}
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</div>
)
}
export default Form
Form.test.tsx
import React from 'react';
import Form from './Form';
import {render} from '#testing-library/react';
import userEvent from '#testing-library/user-event';
describe('Form Component', () => {
it('able to change the date', () => {
const { getByRole } = render(<Form/>)
const date1Field = getByRole('textbox', { name: /date1/i })
act(() => userEvent.type(date1Field, '01/01/1990'))
... any other action to submit the form
})
})
However, my terminal showed me, which is the same for both date, which it was unable to detect the input field:
TestingLibraryElementError: Unable to find an accessible element with the role"textbox" and name "/date1/i"
textbox:
Name=""
<input
class=''
name='date1'
type='text'
value='05/23/2021'
/>
Name=""
<input
class=''
name='date2'
type='text'
value='05/23/2021'
/>
There is an issue with the library itself and not being able to pass in any ARIA props into the datepicker in react-datepicker.
With using the other library you mentioned react-day-picker it is possible to pass props into the input and set aria labels.
import DayPickerInput from 'react-day-picker/DayPickerInput';
<DayPickerInput inputProps={{'aria-label':'Date input 2'}} />
Sandbox: https://codesandbox.io/s/react-day-picker-examplesinput-forked-lj8pp
For anyone who is looking for a solution which I have adopted Jonathan S. answer,
Form.tsx
import React, { useState } from 'react';
import DayPickerInput from "react-datepicker/DayPickerInput";
import { Form } from 'react-bootstrap';
import "react-day-picker/lib/style.css";
const Form = () => {
const [data, setData] = useState({ date1: new Date(), date2: new Date() })
return (
<div>
<Form>
...some other fields
<Form.Group controlId="date1">
<Form.Label>Date1</Form.Label>
<DayPickerInput inputProps={{ 'aria-label': 'date1' }} value={data.date1} onChange={(date: Date) => setData({...data, date1: date})}/>
</Form.Group>
<Form.Group controlId="date2">
<Form.Label>Date2</Form.Label>
<DayPickerInput inputProps={{ 'aria-label': 'date2' }} value={data.date2} onChange={(date: Date) => setData({...data, date2: date})}/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</div>
)
}
export default Form
Form.test.tsx
import React from 'react';
import Form from './Form';
import {render} from '#testing-library/react';
import userEvent from '#testing-library/user-event';
describe('Form Component', () => {
it('able to change the date', () => {
const { getByLabelText } = render(<Form/>)
let date1Field = getByLabelText('date1') as HTMLInputElement
// Selects your default value of the date field
date1Field.setSelectRange(0, date1Field.value.length)
// Replaces it
userEvent.type(date1Field, '1990-01-01')
... any other action to submit the form
})
})
It might not be needed but in case, you can pass a prop placeholderText='some text' and then get the input using screen.getByPlaceholderText('some text');
try this solution, this perfectly works for me
const startDate = await container.find('.ant-picker-input input').first();
await startDate.simulate('mousedown');
await waitFor(async () => {
await container.update();
const dateChart = await container.find('.ant-picker-today-btn');
await dateChart.simulate('click');
});
After Trying many solution, and trying and error.
I found a solution that work perfectly fine for me.
describe('Date Picker Test', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<Router>
<DatePikerFunction />
</Router>
</Provider>,
);
});
it('Date Picker Change', async () => {
const datePicker = await wrapper.find('.ant-picker-input input').first();
await datePicker.simulate('mousedown');
await waitFor(async () => {
await wrapper.update();
const today = await wrapper.find('.ant-picker-cell-today'); // put classname that is used to select the perticular date
const next = today.nextElementSibling;
await next.click();
});
});
});
Here I have find the today's date and then selected tomorrow date.
You can find base on class or any thing you want and use it.
Hope It will work for you too.
Thanks

How do get value of an independent controlled component in react-hook-form?

I'm try to get the selected value from the datepicker of the antd. To get a value from the datepicker of the antd, I used it because it was said that I had to use the controller.
import React from "react";
import { DatePicker } from "antd";
import moment from "moment";
// The module moment.js is large, so I made it an independent component.
export default function InputDatePicker({ name, date, control }) {
const dateFormat = "YYYY-MM-DD";
const initialValue = undefined;
return (
// I tried to use the controller inside the InputDatePicker component,
// but it does not recognize the value.
// <Controller
// as={<DatePicker format={dateFormat} />}
// name="installDate"
// type="date"
// control={control}
// />
<DatePicker name="installDate" format={dateFormat} />
);
}
import { useForm, Controller } from "react-hook-form";
export default function SubmitForm() {
const { register, handleSubmit, control, watch } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
// Input Other components are simply components that receive and render values.
<InputOther
label="date"
component={
<Controller
as={<InputDatePicker defaultValue="" />}
name="date"
defaultValue=""
control={control}
/>
}
/>
<button type="submit">submit</button>
</form>
);
}
However, this will only return the default "", even if the value is selected. How can I get the value of an independent datpicker component? I'd be very grateful if you could tell me how to solve this.
I Don't Know Which Component You Used But I Prefered Following Approche:
Step 1:
npm install react-datepicker --save
Step 2:
import React, { useState } from "react";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
// CSS Modules, react-datepicker-cssmodules.css
// import 'react-datepicker/dist/react-datepicker-cssmodules.css';
const Example = () => {
const [startDate, setStartDate] = useState(new Date());
return (
<DatePicker selected={startDate} onChange={date => setStartDate(date)} />
);
};
& This Will Work For ME.

Cannot read property 'focus' of null error when extracting component

I would like to implement Algolia search with Ant Design Autocomplete. But I get Cannot read property 'focus' of null error when I try to extract the SearchInput component (without extraction, i. e. when I leave it in the same file, it works fine). Here is the working code:
import React, { useState } from 'react'
import { AutoComplete, Input } from 'antd'
const SearchAutocomplete = connectAutoComplete(
({ hits, currentRefinement, refine }) => {
...
return (
<AutoComplete
options={options}
onSelect={onSelect}
onSearch={handleSearch}
open={open}
>
<Input
value={currentRefinement}
onChange={e => refine(e.currentTarget.value)}
/>
</AutoComplete>
);
}
);
But when I move Input to a separate component like this it doesn't work:
import React, { useState } from 'react'
import { AutoComplete } from 'antd'
import SearchInput from './SearchInput'
const SearchAutocomplete = connectAutoComplete(
({ hits, currentRefinement, refine }) => {
...
return (
<AutoComplete
options={options}
onSelect={onSelect}
onSearch={handleSearch}
open={open}
>
<SearchInput value={currentRefinement} onChange={e => refine(e.currentTarget.value)}/>
</AutoComplete>
);
}
);
And the SearchInput component itself:
import React from 'react'
import { Input } from 'antd'
const SearchInput = props => {
const { value, onChange} = props;
return (
<Input
value={value}
onChange={onChange}
/>
)
}
Here is the link to codesandbox with the extracted component. How can I fix this error?
Adding React.forwardRef() to SearchInput solved the issue:
const SearchInput = React.forwardRef((props, ref) => {
const { onChange } = props;
return (
<Input.Search
onChange={onChange}
ref={ref}
/>
)
})

Formik with react-naitve, using useField?

I don't use formik's Field with react-native
because doc says <Field /> will default to an HTML <input /> element
I'd like to use useField (https://stackoverflow.com/a/58650742/433570)
and wonder useField is usable with react-native?
import React from "react";
import { useField, useFormikContext } from "formik";
import DatePicker from "react-datepicker";
export const DatePickerField = ({ ...props }) => {
const { setFieldValue } = useFormikContext();
const [field] = useField(props);
return (
<DatePicker
{...field}
{...props}
selected={(field.value && new Date(field.value)) || null}
onChange={val => {
setFieldValue(field.name, val);
}}
/>
);
};

Resources