I'm using material ui autocomplete textfield in my project inside a dialog.
The problem is, when i set a defaultValue or a first value, you can see the placeholder moving on the opening of the dialog.
https://codesandbox.io/embed/material-ui-autocomplete-forked-xwd3k?fontsize=14&hidenavigation=1&theme=dark
Here is the code
import React from "react";
import ReactDOM from "react-dom";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { TextField } from "#material-ui/core";
import DialogTitle from "#material-ui/core/DialogTitle";
import Dialog from "#material-ui/core/Dialog";
import Button from "#material-ui/core/Button";
import "./styles.css";
function App() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = (value) => {
setOpen(false);
};
return (
<>
<Dialog
onClose={handleClose}
aria-labelledby="simple-dialog-title"
open={open}
>
<DialogTitle id="simple-dialog-title">Set backup account</DialogTitle>
<Autocomplete
freeSolo
id="free-solo-demo"
defaultValue={"a"}
options={["a", "b", "c"]}
renderInput={(params) => (
<TextField
{...params}
label="freeSolo"
margin="normal"
variant="outlined"
fullWidth
/>
)}
/>
</Dialog>
<div>
<br />
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open simple dialog
</Button>
</div>
</>
);
}
Is there a way to see the placeholder already fixed when the dialog is open? I have an other autocomplete that is with multiple, and this textfield is not giving me this error at all.
Add the following InputLabelProps to the TextField component.
<TextField
{...params}
InputLabelProps={{ // this part
shrink: true
}}
label="freeSolo"
margin="normal"
variant="outlined"
fullWidth
/>
If you check the implementation of the TextField component, you can see that the input label prop gets notched if the shrink parameter is truthy.
Edit: Which means that if the field becomes empty you will have to manage that yourself.
Related
I am having trouble trying to implement a customized dropdown that does not seem to be a built in feature in Material UI. I am using Material UI for all these components btw. So I have a TextField with a Chip for the End Adornment.
The expected behavior is that if the user were to click on the chip, there should be a dropdown that pops up under the TextField where the user can select the type of vehicle. However, I don't think there is a built in Material UI way to do this. Any suggestions? Any suggestions would be appreciated. Thanks!
You can implement it like this:
https://codesandbox.io/s/hungry-golick-kdoylz?file=/src/App.js
import { useState } from "react";
import { TextField, Chip, InputAdornment, Menu, MenuItem } from "#mui/material";
import KeyboardArrowDown from "#mui/icons-material/KeyboardArrowDown";
export default function App() {
const [selectedItem, setSelectedItem] = useState("Jeep");
return (
<TextField
label="With normal TextField"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<ChipDropDown
items={["Jeep", "Volvo", "Ferrari"]}
selectedItem={selectedItem}
onChanged={setSelectedItem}
/>
</InputAdornment>
)
}}
variant="filled"
/>
);
}
const ChipDropDown = ({ items, selectedItem, onChanged }) => {
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (item) => {
onChanged(item);
setAnchorEl(null);
};
return (
<div>
<Chip
label={selectedItem}
onClick={(e) => setAnchorEl(e.currentTarget)}
onDelete={(e) => e}
deleteIcon={<KeyboardArrowDown />}
/>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={(e) => setAnchorEl(null)}
>
{items.map((item) => (
<MenuItem key={item} onClick={(e) => handleClick(item)}>
{item}
</MenuItem>
))}
</Menu>
</div>
);
};
I'm using the material-UI Autocomplete component in my react app and I want to show a div when hovering on an option from the list (like in the image)
Show image
any help with that, please
you need to use the renderOption attribute with a Tooltip like this :
import React from "react";
import TextField from "#material-ui/core/TextField";
import Autocomplete from "#material-ui/lab/Autocomplete";
import { Button, Tooltip } from "#material-ui/core";
import { Info } from "#material-ui/icons";
export default function ComboBox() {
const defaultProps = {
options: top100Films,
getOptionLabel: option => option.title
};
return (
<Autocomplete
{...defaultProps}
id="list-option"
debug
ListboxComponent={props => <div {...props} />}
getOptionDisabled={({ year }) => year > 2000}
renderOption={({ title, year, ...props }) => {
return (
<div>
<Tooltip title={`Too Old ${year}`} placement="bottom">
<div>
<Button endIcon={<Info />} component="li" {...props} fullWidth>
{title} - {year}
</Button>
</div>
</Tooltip>
</div>
);
}}
renderInput={params => (
<TextField
{...params}
label="Broken Disabled Tooltips"
margin="normal"
fullWidth
/>
)}
/>
);
}
Working on a tutorial atm that involves react material-ui tables that also has a search input textfield. What I am trying to add to it, is a button that will reset the table report but also clear the search input textfield.
It is the clearing of the search textfield that I am having trouble with.
They are using this code as a separate component library called Controls.input:
import React from 'react'
import { TextField } from '#material-ui/core';
export default function Input(props) {
const { name, label, value,error=null, onChange, ...other } = props;
return (
<TextField
variant="outlined"
label={label}
name={name}
value={value}
onChange={onChange}
{...other}
{...(error && {error:true,helperText:error})}
/>
)
}
The main search code is as follows where I have also added a button
<Controls.Input
id="name"
label="Search Name"
className={classes.searchInput}
InputProps={{
startAdornment: (<InputAdornment position="start">
<Search />
</InputAdornment>)
}}
onChange={handleSearch}
/>
<Button
onClick={handleClear}
className="materialBtn"
>
Clear
</Button>
At this point, I am not sure how to reference/target the search input field as part of the handleClear function, in-order to clear it's contents?
const handleClear = () => {
????
}
Do I need to use useState()?
You are right with having to put the value into state. Based on what you have supplied it seems that your state needs to be in your parent component. So something like this should work
import { useState } from 'react'
const ParentComponent = () => {
const [value, setValue] = useState('')
const handleClear = () => {
setValue('')
}
const handleSearch = (event) => {
setValue(event.target.value)
}
return (
<>
<Controls.Input
id="name"
label="Search Name"
className={classes.searchInput}
value={value}
onChange={handleSearch}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<Search />
</InputAdornment>
),
}}
/>
<Button onClick={handleClear} className="materialBtn">
Clear
</Button>
</>
)
}
I currently using a Select component in my app.
I built a custom modal component that I want to launch instead of the list items when the select is clicked. Is there a way to override the handler for clicks on all portions of the component, such as icon, text field, and dropdown arrow to launch my modal? I want to take just the styling of this component essentially and override the onChange and MenuItem stuff.
<Select
value={props.selectedValue}
onChange={props.onTimeChange}
displayEmpty
startAdornment={
<InputAdornment position="start">
<DateRangeIcon />
</InputAdornment>
}
>
{/* DONT USE THESE MENU ITEMS AND USE CUSTOM MODAL INSTEAD */}
{/*<MenuItem value={-1} disabled>*/}
{/* Start Date*/}
{/*</MenuItem>*/}
{/*<MenuItem value={1}>Last Hour</MenuItem>*/}
{/*<MenuItem value={24}>Last Day</MenuItem>*/}
{/*<MenuItem value={24 * 7}>Last Week</MenuItem>*/}
{/*<MenuItem value={24 * 31}>Last Month</MenuItem>*/}
{/*<MenuItem value={''}>All</MenuItem>*/}
</Select>
In order for it to make sense to leverage Select while using an alternative display for the options, it is important that you provide it with menu items for all the allowed values, because the display of the selected item is based on finding a matching MenuItem for the current value (though it would also be possible to provide the Select with a single MenuItem with a dynamic value and text matching whatever the current selected value is).
You can use a "controlled" approach for managing the open state of the Select using the open and onOpen props (you can leave out onClose since the close should always be triggered by your custom display of the options). This way, rather than trying to override the different events that cause the Select to open, you just let it tell you when it should open (via the onOpen prop), but instead of opening the Select, leave its open prop as always false and only open your custom popup.
Here's a working example:
import React from "react";
import InputAdornment from "#material-ui/core/InputAdornment";
import Button from "#material-ui/core/Button";
import DateRangeIcon from "#material-ui/icons/DateRange";
import Popover from "#material-ui/core/Popover";
import Box from "#material-ui/core/Box";
import Select from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
export default function SimplePopover() {
const [value, setValue] = React.useState(1);
const [open, setOpen] = React.useState(false);
const selectRef = React.useRef();
const handleSelection = newValue => {
setValue(newValue);
setOpen(false);
};
return (
<Box m={2}>
<Select
ref={selectRef}
value={value}
onChange={e => setValue(e.target.value)}
displayEmpty
open={false}
onOpen={() => setOpen(true)}
startAdornment={
<InputAdornment position="start">
<DateRangeIcon />
</InputAdornment>
}
>
<MenuItem value={1}>Last Hour</MenuItem>
<MenuItem value={24}>Last Day</MenuItem>
<MenuItem value={24 * 7}>Last Week</MenuItem>
<MenuItem value={24 * 31}>Last Month</MenuItem>
<MenuItem value={""}>All</MenuItem>
</Select>
<Popover
id="simple-popover"
open={open}
anchorEl={selectRef.current}
onClose={() => handleSelection(value)}
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
transformOrigin={{
vertical: "top",
horizontal: "left"
}}
>
<Button onClick={() => handleSelection(1)}>Last Hour</Button>
<Button onClick={() => handleSelection(24)}>Last Day</Button>
</Popover>
</Box>
);
}
Here's a second example using a single, dynamic MenuItem for the selected value instead of a comprehensive set of menu items:
import React from "react";
import InputAdornment from "#material-ui/core/InputAdornment";
import Button from "#material-ui/core/Button";
import DateRangeIcon from "#material-ui/icons/DateRange";
import Popover from "#material-ui/core/Popover";
import Box from "#material-ui/core/Box";
import Select from "#material-ui/core/Select";
import MenuItem from "#material-ui/core/MenuItem";
export default function SimplePopover() {
const [value, setValue] = React.useState(1);
const [text, setText] = React.useState("Last Hour");
const [open, setOpen] = React.useState(false);
const selectRef = React.useRef();
const handleSelection = (newValue, newText) => {
setValue(newValue);
setText(newText);
setOpen(false);
};
return (
<Box m={2}>
<Select
ref={selectRef}
value={value}
onChange={e => setValue(e.target.value)}
displayEmpty
open={false}
onOpen={() => setOpen(true)}
startAdornment={
<InputAdornment position="start">
<DateRangeIcon />
</InputAdornment>
}
>
<MenuItem value={value}>{text}</MenuItem>
</Select>
<Popover
id="simple-popover"
open={open}
anchorEl={selectRef.current}
onClose={() => handleSelection(value)}
anchorOrigin={{
vertical: "bottom",
horizontal: "left"
}}
transformOrigin={{
vertical: "top",
horizontal: "left"
}}
>
<Button onClick={() => handleSelection(1, "Last Hour")}>
Last Hour
</Button>
<Button onClick={() => handleSelection(24, "Last Day")}>
Last Day
</Button>
</Popover>
</Box>
);
}
I am very new to React so this may seem a little trivial. I have a delete icon in one file which when clicked, I am trying to program a confirmation dialog box. Using the source of their website: https://material-ui.com/components/dialogs/. My file is comprises of a listview:
ListView:
import React from 'react';
import PropTypes from 'prop-types';
import { List, ListItem, ListItemText, ListItemAvatar, Avatar, ListItemSecondaryAction, IconButton } from '#material-ui/core';
import DeleteIcon from '#material-ui/icons/Delete';
import AlertDialog from './AlertDialog'
// Import CSS
import './ListViewer.css'
export function ListViewer({ objects}) {
return (
<div className='list-viewer'>
<List>
<ListItem alignItems="center" divider key={obj.id}>
<ListItemText primary={objects.name} />
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete" onClick={handleClickOpen()}>
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
</List>
</div>
);
}
AlertDialog.js:
import React from 'react';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
export default function AlertDialog() {
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<div>
{/* <Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open alert dialog
</Button> */}
<Dialog
open={open}
onClose={handleClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{"Are you sure you want to delete this object?"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Deleting this object will permanently remove it
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleClose} color="primary" autoFocus>
Delete
</Button>
</DialogActions>
</Dialog>
</div>
);
}
As you can see in AlertDialog, there was initially a button which triggers the dialog to open. Instead from my other file, when the delete icon is clicked, I am trying to trigger the dialog. How can I do this? I have imported AlertDialog and AlertDialog.handleClickOpen but this does not work as handleClickOpen is not a function
You can pass open and onClose via props into AlertDialog.
function AlertDialog(props) {
const { open, onClose } = props
return (
<Dialog
open={open}
onClose={onClose}
>
{/* Dialog content */}
</Dialog>
Then, simply use it in ListView:
function ListView() {
const [dialogIsOpen, setDialogIsOpen] = React.useState(false)
const openDialog = () => setDialogIsOpen(true)
const closeDialog = () => setDialogIsOpen(false)
return (
<div className='list-viewer'>
<List>{/* Now you can set dialogIsOpen here */}</List>
<AlertDialog open={dialogIsOpen} onClose={closeDialog} />
</div>
)
}
There are a few steps how I would do it the first is that I would do instead of a open and close function I would do one as this:
const toggleDialog = useCallback(() => {
setOpen(!open);
}, [open]);
The useCallback function makes it that it only creates a new function when the parameter in the [] changes, means when open changes.
Sadly #Code-Apprentice was faster and metioned the rest to it.
<IconButton edge="end" aria-label="delete" onClick={handleClickOpen()}>
The onClick handler here must be in the same class that renders the <IconButton>. Also, remove the parentheses to set the onClick prop to the function instead of its return value:
onClick={handleClickOpen}