I tried creating a custom MUI Text field with the following code:
const CustomTextField = styled(TextField)(() => ({
height: '56px',
width: '505px',
'& input + fieldset': {
borderRadius: '12px',
borderColor: 'white',
color: 'black'
}
}));
export function SearchTextField(props: TextFieldProps): JSX.Element {
return (
<CustomTextField
style={{ width: '505px' }}
InputProps={{
style: {
height: '56px'
},
startAdornment: (
<InputAdornment position="start">
<SearchOutlined fontSize="medium" color="info" />
</InputAdornment>
)
}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);
}
When I add a background to the same, the input and the icon I have added is disappearing. I can still type and the input is still being taken but I can't see them.
It's supposed to look like this
But looks like this
Any kind of help is appreciated
Related
for MUI learning purposes I'm creating a simple CRUD app with a modal. That modal contains a simple form with a few TextField and one Select components. THe issue is, that when clicking on the Select component, the modal closes.
Modal:
<ClickAwayListener
onClickAway={handleClickAway}
>
<Box sx={{ marginTop: '80px' }}>
<Button
sx={{
borderRadius: '8px',
backgroundColor: '#fff',
color: '#091fbb',
border: '1px solid #091fbb'
}}
onClick={handleOpen}
>
Add new
</Button>
<Modal
hideBackdrop
open={open}
onClose={handleClose}
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
backgroundColor: '#fff',
border: '1px solid #b9c2ff',
borderRadius: '8px',
height: 'fit-content',
width: 400,
boxShadow: 2,
}}
>
<form
onSubmit={handleSubmit}
style={{
display: 'flex',
flexDirection: 'column',
paddingTop: '12px',
paddingLeft: '18px',
paddingRight: '18px',
paddingBottom: '30px',
}}
>
<Typography variant='h6' sx={{ my: 2, textAlign: 'center' }}>ADD NEW PARTICIPANT</Typography>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Fullname</Typography>
<TextField
variant='standard'
value={fullname}
onChange={(e) => setFullname(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Gender</Typography>
<Select
variant='standard'
value={gender}
MenuProps={{
onClick: e => {
e.preventDefault();
}
}}
onChange={(e) => setGender(e.target.value)}
>
<MenuItem value="None"><em>None</em></MenuItem>
<MenuItem value='Male'>Male</MenuItem>
<MenuItem value='Female'>Female</MenuItem>
<MenuItem value='Other'>Other</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Email</Typography>
<TextField
variant='standard'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Phone nr</Typography>
<TextField
variant='standard'
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
</FormControl>
<FormControl sx={{ my: 1 }}>
<Typography variant='body2'>Description</Typography>
<TextField
variant='standard'
value={description}
onChange={(e) => setDescription(e.target.value)}
multiline
rows={3}
/>
</FormControl>
{ !isLoading && <Button
variant='contained'
type='submit'
sx={{
backgroundColor: '#091fbb'
}}>
Add participant
</Button>}
{ isLoading && <Button
variant='contained'
type='submit'
disabled
sx={{
backgroundColor: '#091fbb'
}}>
Adding participant...
</Button>}
</form>
</Modal>
</Box>
</ClickAwayListener>
Handler functions and states for Modal:
const [open, setOpen] = useState(false);
const [fullname, setFullname] = useState('');
const [gender, setGender] = useState('None');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [description, setDescription] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleOpen = () => {
setOpen(!open);
};
const handleClose = () => {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setOpen(false);
};
const handleClickAway = (e) => {
if (!e.target.classList.contains('MuiMenuItem-root')) {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setOpen(false);
}
};
const handleSubmit = (e) => {
e.preventDefault();
const newParticipant = { fullname, gender, email, phone, description };
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newParticipant)
};
setIsLoading(true);
fetch('http://localhost:8000/participants', requestOptions)
.then(() => {
setFullname('');
setGender('None');
setEmail('');
setPhone('');
setDescription('');
setIsLoading(false);
setOpen(!open);
})
};
Could anyone advise on how to solve this? Adding MenuProps to prevent default behavior on the Select component and the if statement in handleClickAway function didnt help in my case, even though that helped other who were facing the same issue.
Assuming that the goal is to have Select work in Modal without closing it, perhaps the default behavior of Modal could be enough and use of ClickAwayListener may be not be necessary.
Instead of styling Modal directly with the sx prop, try wrap the modal content in a Box and style this container. This preserves the default behavior of Modal, so that clicking on Select would not trigger the closing of it.
Since Modal internally detect click on the backdrop to close itself, consider to style the backdrop with a transparent background instead of disabling it, so that the use of ClickAwayListener could also be omitted.
Demo of simplified example on: stackblitz (excluded all data handling)
<Modal
open={open}
onClose={handleClose}
// 👇 Style the backdrop to be transparent
slotProps={{ backdrop: { sx: { background: "transparent" } } }}
>
<Box
// 👇 Style the container Box for modal content
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
backgroundColor: "#fff",
border: "1px solid #b9c2ff",
borderRadius: "8px",
height: "fit-content",
width: 400,
boxShadow: 2,
}}
>
{/* Modal content here */}
</Box>
</Modal>
This happens because the menu is by default mounted in the DOM outside of the modal HTML hierarchy; the Select component uses a Menu component which in turn uses a Popper component. Looking at the API documentation for Popper:
The children will be under the DOM hierarchy of the parent component.
disablePortal:bool = false
A simple solution is to override this default in MenuProps, which will cause the component to be rendered as a child to the Modal and will no longer trigger the ClickAwayListener callback. I'm not aware of any downsides to this approach.
<Select
variant='standard'
value={gender}
MenuProps={{
disablePortal: true, // <--- HERE
onClick: e => {
e.preventDefault();
}
}}
onChange={(e) => setGender(e.target.value)}
> . . . </Select>
I'm trying to customize the tooltip appearance and the position of the speed dial but get an error when doing both.
const useStyles = makeStyles((theme) => ({
root: {
height: 380,
transform: "translateZ(0px)",
flexGrow: 1,
},
speedDial: {
position: "absolute",
bottom: theme.spacing(2),
right: theme.spacing(0),
},
tooltip: {
backgroundColor: "#37517e",
fontSize: "1.1em",
},
}));
<SpeedDial
ariaLabel="Tutor SpeedDial"
className={classes.speedDial}
icon={<SpeedDialIcon openIcon={<EditIcon />} />}
onClose={handleClose}
onOpen={handleOpen}
open={open}
>
{actions.map((action) => (
<SpeedDialAction
key={action.name}
icon={action.icon}
tooltipTitle={action.name}
TooltipClasses={classes}
onClick={handleClose}
/>
))}
</SpeedDial>
The code actually compiles and works but I get a console error
index.js:1 Material-UI: The key speedDial provided to the classes prop is not implemented in ForwardRef(Tooltip).
You can only override one of the following: popper,popperInteractive,popperArrow,tooltip,tooltipArrow,arrow,touch,tooltipPlacementLeft,tooltipPlacementRight,tooltipPlacementTop,tooltipPlacementBottom.
It's pretty clear that the issue is because ToolTipClasses cannot override the speedDial class but I'm not sure how else to do it.
Any guidance will be much appreciated
Thanks
I came up with solution by by creating a StyledSpeedDial instead so that I could remove the speeddial onbject from the classes style
const StyledSpeedDial = styled(SpeedDial)(({ theme }) => ({
position: "absolute",
"&.MuiSpeedDial-directionUp, &.MuiSpeedDial-directionLeft": {
bottom: theme.spacing(2),
right: theme.spacing(0),
},
}));
<StyledSpeedDial
ariaLabel="Tutor SpeedDial"
icon={<SpeedDialIcon openIcon={<EditIcon />} />}
onClose={handleClose}
onOpen={handleOpen}
open={open}
>
{actions.map((action) => (
<SpeedDialAction
key={action.name}
icon={action.icon}
tooltipTitle={action.name}
TooltipClasses={classes}
onClick={handleClose}
/>
))}
</StyledSpeedDial>
I am trying to style the padding so that the icon is pushed to the far right side in an AutoComplete Material UI component which is currently being overridden by this style:
.MuiAutocomplete-hasPopupIcon.MuiAutocomplete-hasClearIcon .MuiAutocomplete-inputRoot[class*="MuiOutlinedInput-root"]
This is the code:
const useStyles = makeStyles(theme => ({
inputRoot: {
color: "blue",
fontFamily: "Roboto Mono",
backgroundColor: fade("#f2f2f2", 0.05),
"& .MuiOutlinedInput-notchedOutline": {
borderWidth: '2px',
borderColor: "blue"
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderWidth: "2px",
borderColor: "blue"
},
"&.Mui-focused .MuiOutlinedInput-notchedOutline": {
borderWidth: "2px",
borderColor: "blue"
}
}
}));
const textStyles = makeStyles({
formControlRoot: {
fontFamily: "Roboto Mono",
width: "50vw",
color: "#ffffff",
borderRadius: "7px",
position: "relative",
"& label.Mui-focused": {
color: "blue"
},
},
inputLabelRoot: {
color: "#ffffff",
fontFamily: "Roboto Mono",
"&.focused": {
color: "#ffffff"
}
},
});
export default function ComboBox() {
const classes = useStyles();
const textClasses = textStyles();
return (
<Autocomplete
id="combo-box-demo"
classes={classes}
// options={top100Films}
getOptionLabel={option => option.title}
renderInput={params => {
return (
<TextField
{...params}
label="Combo box"
variant="outlined"
classes={{ root: textClasses.formControlRoot }}
fullWidth
InputProps={{
...params.InputProps,
endAdornment: (
<InputAdornment position="end">
<SearchIcon />
</InputAdornment>
)
}}
InputLabelProps={{ classes: {root: textClasses.inputLabelRoot}}}
/>
);
}}
/>
);
}
And this is the result:
You are specifying the endAdornment for the Input, but Autocomplete also tries to specify the endAdornment. Your endAdornment is winning, but the Autocomplete is still trying to apply all of the CSS related to its end adornment (space for the popup icon and clear icon).
You can turn off the CSS related to the Autocomplete's end adornment by passing the props that turn off those features:
<Autocomplete
disableClearable
forcePopupIcon={false}
v4 CodeSandbox: https://codesandbox.io/s/autocomplete-with-custom-endadornment-86c87?file=/src/App.js
v5 CodeSandbox: https://codesandbox.io/s/autocomplete-with-custom-endadornment-euzor?file=/src/App.js
Alternatively, if you want to keep the clear icon and/or force-popup icon (arrow-drop-down icon), you can leverage cloneElement to add the search icon to the existing end adornment as shown below.
import React from "react";
import Autocomplete from "#mui/material/Autocomplete";
import TextField from "#mui/material/TextField";
import SearchIcon from "#mui/icons-material/Search";
import { styled } from "#mui/material/styles";
const StyledSearchIcon = styled(SearchIcon)`
vertical-align: middle;
`;
function addSearchIconToEndAdornment(endAdornment) {
const children = React.Children.toArray(endAdornment.props.children);
children.push(<StyledSearchIcon />);
return React.cloneElement(endAdornment, {}, children);
}
export default function ComboBox() {
return (
<Autocomplete
id="combo-box-demo"
options={top100Films}
getOptionLabel={(option) => option.title}
renderInput={(params) => {
return (
<TextField
{...params}
label="Combo box"
variant="outlined"
fullWidth
InputProps={{
...params.InputProps,
endAdornment: addSearchIconToEndAdornment(
params.InputProps.endAdornment
)
}}
/>
);
}}
/>
);
}
I am trying to reduce gap between stars but no luck.
Code below for your ref
<MuiThemeProvider >
<Rating
onChange={() => console.log('onChange')}
value={4}
max={5}
iconFilled={<ToggleStar color={colors.green500} />}
iconHovered={<ToggleStarBorder color={colors.green500} />}
iconNormal ={<ToggleStarBorder color={colors.black300}/>}
className={classes.Rating}
spacing ={0}
onChange={(value) => console.log(`Rated with value ${value}`)}
/>
</MuiThemeProvider >
How can I resolve this?
Use the itemStyle and itemIconStyle props:
const smallDistanceStyle = {
width: 30,
height: 30,
padding: 5
}
const iconStyle = {
width: ...,
height: ...
}
<Rating
itemStyle={smallDistanceStyle}
itemIconStyle={iconStyle}
...
/>
I know this question was asked a while ago, but I found that
<Rating
value={5}
readOnly
sx={{
fontSize: "5rem",
"& .MuiRating-icon": {
width: '4.5rem'
}
}}
icon={<StarRateIcon fontSize="5rem" sx={{ color: "black" }} />}
/>
worked for me. It was able to bring the stars a little closer together.
Country select of
autocomplete demo at material-ui
uses react-select and material-ui controls,
shows multiline text, select control changes it's dimensions when country doesn't fit in one line.
I see this behaviour at CodeSandbox when I decrease width of web browser.
How can I modify demo so that country will always fit in one line,
select control will not change it's dimensions?
TextField has props multiline, rows and rowsMax props that can be changed.
If that isn't what you need then you could add the following css to the text in the TextField so the text does not wrap:
overflow: hidden;
white-space: nowrap;
I managed this by mixing a few different things:
First create a class like so:
const useStyles = makeStyles((theme: Theme) =>
createStyles({
closed: {
flexWrap: "nowrap",
overflowX: "hidden",
},
// Add a linear gradient behind the buttons and over the Chips (if applies)
endAdornment: {
background:
"linear-gradient(90deg, rgba(255,255,255,0) 0%, rgba(255,255,255,.6) 22%, rgba(255,255,255,1) 60%)",
bottom: 0,
display: "flex",
alignItems: "center",
right: "0 !important",
paddingRight: 9,
paddingLeft: theme.spacing(2),
top: 0,
},
})
);
Then in your static function add this :
const onOpenChange = (open: boolean | null) => {
setIsOpen(open);
};
const inputStyle = clsx({
[classes.closed]: !isOpen, //only when isOpen === false
});
Finally on the Autocomplete component itself use:
classes={{ inputRoot: inputStyle, endAdornment: classes.endAdornment }}
onOpen={() => onOpenChange(true)}
onClose={() => onOpenChange(false)}
If you are wondering how to make each option be displayed in just one line with ellipsis, you can do the follow:
<Autocomplete
...
getOptionLabel={(option: any) => `${option.label} (${option.code})`}
renderOption={(option) => (
<React.Fragment>
<div style={{ textOverflow: 'ellipsis', overflow: "hidden", whiteSpace: "nowrap" }}>
{option.label} ({option.code})
</div>
</React.Fragment>
)}
...
/>
For the Country Demo example, you can check what I did here: https://codesandbox.io/s/autocomplete-with-ellipsis-i8hnw