Non-MUI component as renderInput prop of TimePicker (#mui/x-date-pickers) - reactjs

I have the following implementation of MUI's TimePicker:
<TimePicker
value={time}
inputFormat="HH:mm"
views={["hours", "minutes"]}
onChange={(newValue) => {
newValue && setResultTime(newValue);
}}
renderInput={(props) => {
console.log(props);
return (
<InputWrapper>
<Input ref={props.inputRef} tw="w-16" />
<FontAwesomeIcon
icon={faAngleDown}
tw="text-company-light-1 opacity-80 text-sm px-2 cursor-pointer"
/>
</InputWrapper>
);
}}
/>
I am unable to use a controlled version of this by specifying isOpen because the outside click handler appears to fire when I click on the component and that causes it to close. That is, wrapping it in:
<OutsideClickHandler onOutsideClick={() => setTimePickerOpen(false)}>
...
</OutsideClickHandler>
causes it to close, even when clicking on the component itself.
The props to renderInput do not appear to contain an onClick and so I have no idea how to cause my component to respond to clicks. How can this be achieved?

Related

React Hook Form reset Controller (value) to default

Here is an example of my component, wrapped into Controller
<div className="mt-1">
<Controller
control={control}
name="selectedBirthMonth"
defaultValue={selectedMonth}
render={({ field }) => (
< SelectBirthMonth
field={field}
startYear={startYear}
selectedYear={selectedYear}
months={months}
value={selectedMonth}
reducedMonths={reducedMonths}
onChange={useEffect(() => monthSelect(field.value)), [selectedMonth]}/>
)}
/>
</div>
For now I am just trying to reset it with a button. Later I'd love to conditionally reset, depending on the value in a sibling component. Would be great to get it work with the button in the first place:
<input
style={{ display: "block", marginTop: 20 }}
type="button"
onClick={() =>
reset(selectedBirthMonth)
}
value="Reset with values"
/>
I get en error: selectedBirthMonth is not defined. Bassicaly, what I am aming for,on click, the whole component goes to default / a state it had when first rendered. What am I missing?
reset didn't do the job somehow, but setValue did. However I am setting to a value, not to the original state after first render, but in my case it's same.
<input
style={{ display: "block", marginTop: 20 }}
type="button"
onClick={() =>
etValue('selectedBirthMonth', lastAvaliableMonth)
}
value="Reset with values"
/>

React.JS. How can I apply material theme on child component?

This is my JSX:
<FormControl>
<ButtonGroup className="groupedHorizontal">
<InputLabel htmlFor="category">Category:</InputLabel>
<Select onChange={(event) => that.handleCategoryChange(event)} native={true} id="category">
<option></option>
{catOptions}
</Select>
<BrandsPopup />
<Button onClick={(e) => that.removeCategory(e)}>Del</Button>
</ButtonGroup>
</FormControl>
The BrandsPopup is a component which render a material-ui Button within <React.Fragment>. The select and the "Del" button are fine bordered as ButtonGroup elements. The problem is, BrandsPopup is not bordered and does not appear as part of the group. How to apply ButtonGroup styles on the button, rendered from the child component?
ButtonGroup uses cloneElement and thereby assigns its own props to its children. You should be able to log them to the console inside BrandsPopup and then just need to assign them to your button component. It is, of course, possible that this conflicts with how you are using BrandsPopup elsewhere in your app.
And if BrandsPopup indeed only contains one Button component you don't need the Fragment wrapper.
<ButtonGroup className="groupedHorizontal">
<BrandsPopup />
</ButtonGroup>
const BrandsPopup = (props) => (
<React.Fragment>
<Button
// these come from ButtonGroup 🧙
className={props.className}
color={props.color}
variant={props.variant}
>
click me
</Button>
</React.Fragment>
);

How do I use the SpeedDial to upload a file?

(this seem to have been asked previously but I couldn't find any hint on if it was actually answered)
MUI has a good demo for creating upload buttons which boils down to:
<input accept="image/*" className={classes.input} id="icon-button-file" type="file" />
<label htmlFor="icon-button-file">
<IconButton color="primary" aria-label="upload picture" component="span">
<PhotoCamera />
</IconButton>
</label>
What I wonder is how to implement the same using the Speed Dial. Inherently the SpeedDialAction seems to materialize as a <button/>, but it's not possible to e.g. wrap the SpeedDialAction in a <label htmlFor /> as its parent will try to set some props on it and will fail.
So how do I initiate the file selection from within the Speed Dial or a FAB in general?
You can create a wrapper component that forwards props to SpeedDialAction.
function UploadSpeedDialAction(props) {
return (
<React.Fragment>
<input
accept="image/*"
style={{ display: "none" }}
id="icon-button-file"
type="file"
/>
<label htmlFor="icon-button-file">
<SpeedDialAction
icon={<CloudUploadIcon />}
tooltipTitle="upload"
component="span"
{...props}
></SpeedDialAction>
</label>
</React.Fragment>
);
}
https://codesandbox.io/s/material-demo-forked-h6s4l
(Note to future readers: For v5, time allowing, we hope to rationalise where props rather than context are used to control children, in order to solve exactly this kind of issue. So check whether this solution is still needed.)
It is - in my knowledge - not possible to add the htmlFor in any way. So what I would do is to add a hidden input type file and then add a ref to it. Then in the onclick of the SpeedDialAction button I would call a handler function that clicks on the input ref. Like this:
const inputRef = useRef();
const handleFileUploadClick = () => {
inputRef.current.click();
};
Then your SpeedDialAction:
<SpeedDialAction
onClick={handleFileUploadClick}
... the rest of your props
/>
And then finnaly your actual input:
<input
style={{ display: "none" }}
ref={inputRef}
accept="image/*"
id="contained-button-file"
multiple
type="file"
/>
Working demo: https://codesandbox.io/s/material-demo-forked-f9i6q?file=/demo.tsx:1691-1868

React Bootstrap Typeahead children onclick

I have been using a Premade React-Bootstrap Typeahead search-bar found here
I am having trouble accessing the child elements from the dropdown. I want to be able to have an Onclick function for the dropdown items, but it doesn't seem like I have access to the children.
Below is the code I have currently, where I use typeahead
<div className="search-bar">
<Typeahead
id="sample"
options= {cities.map((city) => (city))}
labelKey="name"
placeholder="Enter a city"
/>
</div>
How do I get an onclick for these element cities listed?
You can customize menu rendering and behavior by using the renderMenu prop:
<Typeahead
options={options}
renderMenu={(results, menuProps) => (
<Menu {...menuProps}>
{results.map((result, index) => (
<MenuItem
onClick={() => console.log('click!')}
option={result}
position={index}>
{result.label}
</MenuItem>
))}
</Menu>
)}
/>
With the menu items exposed, you're free to add your own onClick handler to each item. Here's a working example.

Cannot get Material UI radio buttons to work with Formik

I am trying to use Material UI radio buttons with Formik, and they are not clicking properly. I've reduced the problem to the following example: https://codesandbox.io/s/amazing-currying-s5vn0
If anyone knows what I might be doing wrong, or if there is a bug in either system, then please let me know. When clicking on the buttons in the above example, they do not stay clicked. I have a more complex react functional component that uses other library components, so I cannot include it here. It is behaving a little differently: the buttons stay clicked even after clicking a different button. It may or may not be the same issue. Thanks in advance.
You need to be rendering the radio buttons inside of the FormikRadioGroup component you are rendering as a Formik Field. That way you can actually pass the props being managed by Formik down to the components to be used, as well as allow the RadioGroup component to make sure only one button is clicked at a time. I added an options prop to provide a way to pass an array of radio options and removed all of the elements you were rendering outside of that component:
const FormikRadioGroup = ({
field,
form: { touched, errors },
name,
options,
...props
}) => {
return (
<React.Fragment>
<RadioGroup {...field} {...props} name={name}>
{options.map(option => (
<FormControlLabel value={option} control={<Radio />} label={option} />
))}
</RadioGroup>
{touched[fieldName] && errors[fieldName] && (
<React.Fragment>{errors[fieldName]}</React.Fragment>
)}
</React.Fragment>
);
};
Fork here.
EDIT: Updated the sandbox with an alternate example using a render function as a child within the Field component.
import { FormControlLabel, Radio, LinearProgress } from '#material-ui/core';
import { Formik, Field } from 'formik';
import { RadioGroup } from 'formik-material-ui';
<Formik {...otherProps}>
{({ isSubmitting }) => (
<Field component={RadioGroup} name="activity">
<FormControlLabel
value="painting"
control={<Radio disabled={isSubmitting} />}
label="Painting"
disabled={isSubmitting}
/>
<FormControlLabel
value="drawing"
control={<Radio disabled={isSubmitting} />}
label="Drawing"
disabled={isSubmitting}
/>
<FormControlLabel
value="none"
control={<Radio disabled={isSubmitting} />}
label="None"
disabled
/>
</Field>
)}
</Formik>;
Now this is documented! Using RadioGroup from formik-material-ui.
https://stackworx.github.io/formik-material-ui/docs/api/material-ui/

Resources