Material UI autocomplete with datepicker inside it not popping - reactjs

I am trying to fit a datetime picker inside the paper of MUI autocomplete.
Tried mui datepicker and tried to force open the datepicker but no luck.
The native seems to work to an extend but it still needs to wire the date selection click to close the poper.
here is demo:
https://codesandbox.io/s/material-demo-forked-pmgzp
Plz advise

I was trying to integrate DatePicker with autocomplete. I did it with useAutoComplete
import * as React from "react";
import { useAutocomplete } from "#mui/base/AutocompleteUnstyled";
import { DateRangePicker } from "mui-daterange-picker";
import { TextField } from "#mui/material";
import InputAdornment from "#mui/material/InputAdornment";
import CalendarMonthOutlinedIcon from "#mui/icons-material/CalendarMonthOutlined";
import moment, { Moment } from "moment";
export interface IDatePickerProps {
name: string;
startDateLabel: string;
endDateLabel: string;
value: [Moment, Moment];
setFieldValue: (
field: string,
value: [Moment, Moment],
shouldValidate?: boolean
) => void;
}
export default function UseAutocomplete({
name = "datepicker",
value = [moment(), moment().add(1, "months")],
startDateLabel = "2022-10-1",
endDateLabel = "2022-12-1",
setFieldValue
}) {
const { getRootProps, getInputLabelProps, getInputProps } = useAutocomplete({
id: "use-autocomplete-demo",
options: []
});
const [open, setOpen] = React.useState(false);
const toggle = () => setOpen(!open);
return (
<div>
<>
<div {...getRootProps()}>
<label {...getInputLabelProps()}>{name}</label>
<TextField
{...getInputProps()}
name={name}
placeholder={`${startDateLabel} - ${endDateLabel}`}
onClick={toggle}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<CalendarMonthOutlinedIcon />
</InputAdornment>
)
}}
value={`${moment(value[0]).format("YYYY-MM-DD")} - ${moment(
value[1]
).format("YYYY-MM-DD")}`}
/>
</div>
{
<DateRangePicker
open={open}
toggle={toggle}
onChange={(range) => {
let selectedValues = [
moment(range.startDate),
moment(range.endDate)
];
setFieldValue(selectedValues);
}}
/>
}
</>
</div>
);
}
Output:

Related

Why onChange event is not working properly in a dropdown when taking values as props?

I'm working on a React typescript project and need to create a dropdown with the necessary name and link to navigate as props, which will be displayed as menu items in my dropdown. But when I defined values in the same dropdown.tsx file, the onChange event worked properly. But when I received values as props, the onChange event won't work.
Here is my DropdownComponent.tsx file
import * as React from 'react';
import OutlinedInput from '#mui/material/OutlinedInput';
import MenuItem from '#mui/material/MenuItem';
import Select, { SelectChangeEvent } from '#mui/material/Select';
import SearchOutlinedIcon from '#mui/icons-material/SearchOutlined';
import { Box, Button, InputBase } from '#mui/material';
// React-Router Imports
import { Link as RouterLink } from 'react-router-dom/';
/* eslint-disable-next-line */
export interface DropdownComponentProps {
dashboardnames: Array<{
name: string,
link: string
}>,
renderDashboard:()=>void
}
export function DropdownComponent(props: DropdownComponentProps) {
const [configName, setConfigName] = React.useState<string>();
const handleChange = (event: SelectChangeEvent<typeof configName>) => {
const {
target: { value },
} = event;
setConfigName(value);
};
return (
<Box sx={{ marginTop:-5}}>
<Select
sx={{width:190,height:36}}
displayEmpty
value={configName}
onChange={handleChange}
input={<OutlinedInput />}
renderValue={(selected) => {
if (!selected) {
return <>Mushroom tunnel 1</>;
}
return selected;
}}
inputProps={{ 'aria-label': 'Without label' }}
>
<MenuItem disabled value="">
Frequently visited
</MenuItem>
<div style={{marginLeft:'20px', display:'flex'}}> <InputBase placeholder='search'/>
<div style={{marginLeft:'10px', marginRight:'10px'}}>
<Button><SearchOutlinedIcon /></Button>
</div>
</div>
{props.dashboardnames.map((page) => (
<MenuItem
component={RouterLink}
to={page.link}
key={page.name}
onClick={props.renderDashboard}
>
{page.name}
</MenuItem>
))}
</Select>
</Box>
);
}
export default DropdownComponent;
What's the mistake I've made here? Can someone help me with this issue? Thank you in advance!

React Hook Form w/ FormProvider and MUI - Not working

I have been trying to abstract my form components for some time now. I took the example of RHF JS and rewrote it in Typescript.
Unfortunately, I do not get any output of the validation results. When I submit the form nothing happens either. My understanding is that I need to pass the FormContext through to the last component.
Can anyone with experience help me?
App.tsx
import { yupResolver } from "#hookform/resolvers/yup";
import { Button, Grid, TextField } from "#mui/material";
import { memo } from "react";
import {
FormProvider,
useForm,
useFormContext,
UseFormReturn,
} from "react-hook-form";
import { object, SchemaOf, string } from "yup";
type FormData = {
name: string;
};
const schema: SchemaOf<FormData> = object().shape({
name: string()
.required("Input is Required")
.matches(/^[a-zA-Z0-9]+$/, "Only alphanummeric"),
});
type TextInputProps = {
methods: UseFormReturn;
name: string;
label: string;
};
// we can use React.memo to prevent re-render except isDirty state changed
const TextFieldMemo = memo(
({ methods, name, label }: TextInputProps) => (
<TextField
label={label}
variant="outlined"
error={!!methods.formState.errors[name]}
helperText={
(methods.formState.errors[name]?.message as unknown as string) ?? ""
}
fullWidth
margin="normal"
{...methods.register(name)}
InputLabelProps={{
shrink: true,
}}
FormHelperTextProps={{
sx: {
position: "absolute",
bottom: "-1.5rem",
},
}}
/>
),
(prevProps, nextProps) =>
prevProps.methods.formState.isDirty ===
nextProps.methods.formState.isDirty &&
prevProps.methods.formState.errors !== nextProps.methods.formState.errors
);
export const TextFieldMemoContainer = () => {
const methods = useFormContext();
return <TextFieldMemo methods={methods} name="name" label="Name" />;
};
export default function App() {
const methods = useForm<FormData>({
resolver: yupResolver(schema),
mode: "onChange",
});
const { isDirty } = methods.formState;
const onSubmit = (form: FormData) => {
console.log(isDirty);
console.log(form);
};
return (
<Grid container>
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<Grid item>
<TextFieldMemoContainer />
</Grid>
<Grid item>
<Button
type="submit"
disabled={
!methods.formState.isDirty || !methods.formState.isValid
}
>
Submit
</Button>
</Grid>
</form>
</FormProvider>
</Grid>
);
}
Thank you all in advance.
StackBlitz MRE

Textarea input fields in Chakra UI wont submit to react hook form

I am using nextjs (v13), react (v18) chakraUI and react hook form.
If I use Inputs (only), I can submit this form. If I change the description field to be a Textarea (from ChakraUI), the form displays on the page, but will not submit. I get no errors in the console - I can't see what's causing the issue.
Is it possible to submit data from a Textarea via react-hook-form?
import * as React from "react"
import { gql } from "#apollo/client"
import { Button, Stack, Textarea, Text } from "#chakra-ui/react"
import { useRouter } from "next/router"
import { useCreateIssueGroupMutation } from "lib/graphql"
import { useForm } from "lib/hooks/useForm"
import Yup from "lib/yup"
import { ButtonGroup } from "./ButtonGroup"
import { Form } from "./Form"
import { FormError } from "./FormError"
import { Input } from "./Input"
import { Modal } from "antd"
const _ = gql`
mutation CreateIssueGroup($data: IssueGroupInput!) {
createIssueGroup(data: $data) {
id
}
}
`
interface Props {
onClose: () => void
}
const IssueGroupSchema = Yup.object().shape({
title: Yup.string().required(),
description: Yup.string().required(),
})
export function AdminCreateIssueGroupForm(props: Props) {
const router = useRouter()
const [createIssueGroup] = useCreateIssueGroupMutation()
const defaultValues = {
title: "",
description: "",
}
const form = useForm({ defaultValues, schema: IssueGroupSchema })
const handleSubmit = (data: Yup.InferType<typeof IssueGroupSchema>) => {
return form.handler(() => createIssueGroup({ variables: { data: { ...data } } }), {
onSuccess: (res, toast) => {
toast({ description: "Issue group created" })
form.reset()
props.onClose()
},
})
}
return (
<Form {...form} onSubmit={handleSubmit}>
<Stack>
<Input name="title" label="Title" />
// this input works and allows me to submit the form
{/* <Input name="description" label="Description" /> */}
// the next 2 lines do not work. The page renders but the form does not submit
<Text mb='8px' fontWeight="medium" fontSize="sm" > Description</Text>
<Textarea name="description" rows={4} />
<FormError />
<ButtonGroup>
<Button onClick={props.onClose}>Cancel</Button>
<Button
type="submit"
isLoading={form.formState.isSubmitting}
isDisabled={form.formState.isSubmitting}
color="brand.white"
fontWeight="normal"
backgroundColor="brand.orange"
_hover={{
backgroundColor: "brand.green",
color: "brand.white",
}}
>
Create
</Button>
</ButtonGroup>
</Stack>
</Form>
)
}
My Form component has:
import * as React from "react"
import type { FieldValues, UseFormReturn } from "react-hook-form"
import { FormProvider, useFormContext } from "react-hook-form"
import { Box } from "#chakra-ui/react"
import * as Sentry from "#sentry/nextjs"
import { useToast } from "lib/hooks/useToast"
interface FormContainerProps {
onSubmit?: (values: any) => Promise<any> | any
onBlur?: (values: any) => Promise<any> | any
}
const FormContainer: React.FC<FormContainerProps> = (props) => {
const toast = useToast()
const { handleSubmit } = useFormContext()
const onSubmit = async (values: any) => {
try {
if (props.onBlur) {
return await props.onBlur(values)
}
if (props.onSubmit) {
return await props.onSubmit(values)
}
} catch (e) {
console.log(e)
Sentry.captureException(e)
toast({
title: "Application error",
description: "Something went wrong. We have been notified!",
status: "error",
})
return
}
}
return (
<Box
as="form"
w="100%"
{...(props.onSubmit && { onSubmit: handleSubmit(onSubmit) })}
{...(props.onBlur && { onBlur: handleSubmit(onSubmit) })}
>
{props.children}
</Box>
)
}
interface Props<T extends FieldValues> extends UseFormReturn<T>, FormContainerProps {
children: React.ReactNode
isDisabled?: boolean
}
export function Form<T extends FieldValues>({ onSubmit, onBlur, isDisabled, ...props }: Props<T>) {
return (
<FormProvider {...props}>
<fieldset disabled={isDisabled}>
<FormContainer {...{ onSubmit, onBlur }}>{props.children}</FormContainer>
</fieldset>
</FormProvider>
)
}
Input has:
import * as React from "react"
import { useFormContext } from "react-hook-form"
import type { InputProps } from "#chakra-ui/react";
import { FormControl, Input as CInput } from "#chakra-ui/react"
import { InputError } from "./InputError"
import { InputLabel } from "./InputLabel"
interface Props extends InputProps {
name: string
label?: string
subLabel?: string
}
export const Input = ({ label, subLabel, ...props }: Props) => {
const {
register,
formState: { errors },
} = useFormContext()
const fieldError = errors?.[props.name]
return (
<FormControl isInvalid={!!fieldError} isRequired={props.isRequired}>
<InputLabel label={label} subLabel={subLabel} name={props.name} />
<CInput {...register(props.name)} mb={0} {...props} />
<InputError error={fieldError} />
</FormControl>
)
}
Each form component connected to React Hook Form needs to receive a register or be wrapped by a Controller component. Your input component receives this by useFormContext as you mentioned:
<CInput {...register(props.name)} mb={0} {...props} />
However, TextArea component doesn't receive anything from Hook Form, in that case, you need to use the same register('').
An example of this implementation (live on CodeSandbox):
function App() {
const { register, handleSubmit } = useForm({
defaultValues: {
title: "",
description: ""
}
});
return (
<>
<form onSubmit={handleSubmit((data) => console.log(data))}>
<Heading>Welcome to Chakra + TS</Heading>
<p>Title</p>
<Input {...register("title")} />
<p>Description</p>
<Textarea {...register("description")} />
<Button type="submit">Submit</Button>
</form>
</>
);
}
Useful links:
Register
Controller
Live Example

Material-UI pickers 3.3.10 not displaying properly

I am using DatePicker in a custom component and everything works fine including react-hook-form Controllers component for validation. But the top-part of the DatePicker does not display properly. Below is a preview of how it displays.
Here is is how I am using this component to create my own re-useable component. I am at a loss to explain why this is happening. Please any help would be very well appreciated.
Thanks in advance.
import React, { Fragment } from "react";
import { makeStyles } from '#material-ui/core/styles';
import DateFnsUtils from '#date-io/date-fns';
import { MuiPickersUtilsProvider, DatePicker} from '#material-ui/pickers';
import 'date-fns';
import { Control, Controller } from "react-hook-form";
import { Keys } from "../Profile/interfaces";
import { alpha } from '#material-ui/core/styles'
const useStyles = makeStyles((theme) => ({
formControl: {
marginTop: "10px",
marginBottom: "10px",
minWidth: 220,
},
}));
interface Props {
id: string,
label: string,
control: Control<any,any>
required?: boolean,
name: Keys,
requiredMsg?: string,
disabled?: boolean
}
const CustomDate: React.FC<Props> = ({id,label,control, required=false, name, requiredMsg,
disabled = false}) => {
const classes = useStyles();
return(
<div className={classes.formControl}>
<Controller
name={name}
control={control}
rules={{required: required ? requiredMsg : null}}
render={({ field }) =>
<MuiPickersUtilsProvider utils={DateFnsUtils}>
<DatePicker
disabled={disabled}
format="dd/MM/yyyy"
inputVariant="filled"
id={id}
autoOk
label={label}
clearable
disableFuture
{...field}
/>
</MuiPickersUtilsProvider>
}
/>
</div>
);
}
export default CustomDate;

Ionic React-Hook-Forms

Trying to use Ionic React Hook Forms, could anyone help me with the below questions
Trying to build forms dynamically, how can we pass the properties of the ion element dynamically. in the below example i want to pass maxlength and text property (coming from backend / formFields) to IonInput Element
How to pre-populate the value to the input field
Form.tsx
import {
IonContent,
IonPage,
IonText,
IonButton,
} from "#ionic/react";
import React from "react";
import "./Form.css";
import { useForm } from "react-hook-form";
import * as globalStructre from "../Components/Content";
import Input from "../Components/Input";
import Date from "../Components/Date";
import Select from "../Components/Select";
import Textarea from "../Components/Textarea";
import Toggle from "../Components/Toggle";
const Form: React.FC = () => {
const { control, handleSubmit } = useForm();
const formFields: globalStructre.IngressProps[] = [
{
type: "Input",
name: "email",
component: ['type=\'email\''],
label: "Email",
value: "",
},
{
type: "Input",
name: "fullName",
component: ['maxlength = 60', 'type=\'text\''], //properties
label: "Full Name",
value: "test",
},
{
type: "Input",
name: "password",
component: ['maxlength = 12', 'type=\'password\''],
label: "Password",
value: "",
},
];
const registerUser = (data: any) => {
console.log("creating a new user account with: ", data);
};
const buildForm = (field: globalStructre.IngressProps, key: any) => {
let inputElement = null;
switch (field.type) {
case "Input":
inputElement = (
<Input
key={key}
name={field.name}
control={control}
component={field.component}
value={field.value}
label={field.label}
rules={field.rules}
/>
);
break;
}
return inputElement;
};
return (
<IonPage>
<IonContent>
<div className="ion-padding">
<IonText color="muted">
<h2>Create Account</h2>
</IonText>
<form onSubmit={handleSubmit(registerUser)}>
{formFields.map((field, index) =>
//<Input {...field} control={control} key={index} />
buildForm(field, index)
)}
<IonButton expand="block" type="submit" className="ion-margin-top">
Register
</IonButton>
</form>
</div>
</IonContent>
</IonPage>
);
};
export default Form;
Input.tsx
import React, { FC } from "react";
import { IonItem, IonLabel, IonInput, IonText } from "#ionic/react";
import { Controller} from "react-hook-form";
import * as globalStructre from './Content';
import './Input.css';
const Input: FC<globalStructre.IngressProps> = ({
key,
name,
control,
component,
value,
label,
rules,
errors,
}) => {
return (
<>
<IonItem >
{label && <IonLabel position="floating">{label}</IonLabel>}
<Controller
render = {({ onChange, onBlur, value }) => (
<IonInput
key={key}
value ={value}
onIonChange={onChange}
/>
)}
name={name}
control={control}
rules = {rules}
/>
</IonItem>
</>
);
};
export default Input;

Resources