I'm trying to migrate my project from Material-UI v4 to MUI v5 using this How to customize guide from the official website but can't wrap my head how can I rewrite this component with the new guidelines:
...
const useStyles = makeStyles(() => ({
root: {
backgroundColor: "rgba(255, 255, 255, 0.36)",
display: "flex",
height: "100%",
position: "absolute",
textAlign: "center",
top: 0,
width: "100%",
justifyContent: "center",
alignItems: "center",
},
visible: {
visibility: "visible",
},
hidden: {
visibility: "hidden",
},
holder: {
height: 60,
width: 60,
},
}));
interface ILoaderBlockProps {
isLoading: boolean;
className?: string;
testId?: string;
}
type Props = ILoaderBlockProps;
const LoaderBlock: FunctionComponent<Props> = (props: Props) => {
const classes = useStyles({});
const { isLoading, className } = props;
const rootClasses = clsx(
classes.root,
isLoading ? classes.visible : classes.hidden,
className
);
return (
<div className={rootClasses}>
<div className={classes.holder}>
<CircularProgress color={"primary"}/>
</div>
</div>
);
};
...
Since makeStyles is deprecated and I have additional difficulties in MUI v5, I'm trying to stop using it but can't wrap my head around new styling yet.
How could these <div>s be rewritten in new, v5-way since makeStyles is no longer an option?
It seems the only way to do it is to introduce SCSS/CSS file with styles for root and holder.
Here's what I came up with (thanks #morganney for suggestion!):
...
const LoaderBlockRoot = styled("div")(() => ({
backgroundColor: "rgba(255, 255, 255, 0.36)",
display: "flex",
height: "100%",
position: "absolute",
textAlign: "center",
top: 0,
width: "100%",
justifyContent: "center",
alignItems: "center",
}));
const Holder = styled("div")(() => ({
height: 64,
width: 64
}));
const LoaderBlock: FunctionComponent<Props> = (props: Props) => {
const { isLoading } = props;
return (
<LoaderBlockRoot sx={{ visibility: isLoading ? "visible" : "hidden" }}>
<Holder>
<CircularProgress color={"primary"} />
</Holder>
</LoaderBlockRoot>
);
};
...
A bit clumsy for my taste compared to withStyles practices though, I wonder if this could be improved.
Also, I still don't know what to do if I want to override style of component (previously, it was doable by passing prop.className into component).
While the preferred way of styling in v5 is through styled or the sx prop, makeStyles still works. (Why do you say it's deprecated; it's in the v5 docs. You can even get rid of clsx by passing in the props directly into useStyles as shown in the docs example.) The code below works fine for me in v5.
As for overriding, it's still doable even with styled or sx prop. Just target the classname by using the component-slot class name convention (docs)
import { FC, useEffect, useState } from 'react'
import clsx from 'clsx'
import { makeStyles } from '#mui/styles'
import { CircularProgress } from '#mui/material'
const useStyles = makeStyles({
root: {
backgroundColor: 'rgba(255, 255, 255, 0.36)',
display: 'flex',
height: '100%',
position: 'absolute',
textAlign: 'center',
top: 0,
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
visible: {
visibility: 'visible',
},
hidden: {
visibility: 'hidden',
},
holder: {
height: 60,
width: 60,
},
})
export const UseStyles: FC = () => {
const [loading, setLoading] = useState(true)
const classes = useStyles()
useEffect(() => {
setTimeout(() => setLoading(false), 3000)
}, [])
const rootClasses = clsx(
classes.root,
loading ? classes.visible : classes.hidden,
)
return (
<div className={rootClasses}>
<div className={classes.holder}>
<CircularProgress color={'primary'} />
</div>
</div>
)
}
Related
I have created MyComponent( called 'InnerContainer' ) by using MUI system; styled-component. But onClick under the styled component is not working. It is working outside 'InnerContainer' but not working inside 'InnerContainer'. Don't know the reason.. please help me why it's not working.
import { useSortable } from "#dnd-kit/sortable";
import { CSS } from "#dnd-kit/utilities";
import { styled } from "#mui/system";
// import styled from '#emotion/styled'
import DeleteIcon from "#mui/icons-material/Delete";
function DraggableBox(props) {
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: props.id });
// console.log("props", props);
const outerContainer = {
transform: CSS.Transform.toString(transform),
transition,
backgroundColor: props.color,
width: "20%",
height: "25%",
display: "inline-block",
};
const InnerContainer = styled("div")({
width: "100%",
height: "100%",
display: "flex",
justifyContent: "space-between",
alignItems: "end",
fontWeight: "600",
"&:hover svg": {
color: "white",
transform: "scale(1.3)",
transition: "0.5s",
},
" .inBoxName": {
marginLeft: "0.5rem",
},
" .inBoxTrash": {
marginRight: "0.5rem",
},
});
return (
<div style={outerContainer} ref={setNodeRef} {...attributes} {...listeners}>
<InnerContainer>
<div className="inBoxName">{props.name}</div>
<div className="inBoxTrash">
<DeleteIcon onClick={() => props.removeColorBox(props.id)} />
</div>
</InnerContainer>
</div>
);
}
export default DraggableBox;
I am trying to implement a select dropdown option with image by the left side of the text. I used react-select library but the image shows broken.
Here is my code:
import React from 'react'
import Select from 'react-select'
import { components } from 'react-select';
import { login } from "../customSelect/login.png";
const options = [
{
label: 'Option 1',
value: 0,
image: "../customSelect/login.png",
},
{
label: 'Option 2',
value: 1,
image: "../customSelect/login.png",
}
];
const customStyles = {
option: (provided) => ({
...provided,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}),
singleValue: (provided) => ({
...provided,
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}),
}
const { SingleValue, Option } = components;
const IconSingleValue = (props) => (
<SingleValue {...props}>
<img src={props.data.image}
style={{ height: '30px', width:
'30px', borderRadius: '50%', marginRight: '10px' }}/>
{props.data.label}
</SingleValue>
);
const IconOption = (props) => (
<Option {...props}>
<img src={props.data.image}
style={{ height: '30px', width: '30px', borderRadius: '50%',
marginRight: '10px' }}/>
{props.data.label}
</Option>
);
const SelectOption = () => {
return (
<div>
<Select
styles={customStyles}
components={{SingleValue:IconSingleValue,
Option:IconOption}}
options={options}
/>
</div>
)
}
export default SelectOption
this is the result i got
I have this MUI codes that are applied to multiple components. I want to separate it into a single file like 'MuiSetup.js' and then import the file to the component that I am using. I've tried with as React component, however, it does not work and I have zero clues how can I do this.
// Styling TextField
const ValidationTextField = withStyles({
root: {
'& input:valid + fieldset': {
borderColor: '#ff9800',
borderWidth: 1,
},
'& .MuiOutlinedInput-root': {
'&:hover fieldset': {
borderColor: '#ff9800',
},
'&.Mui-focused fieldset': {
borderColor: '#ff9800',
},
},
'& input:invalid + fieldset': {
borderColor: '#ff9800',
borderWidth: 1,
backgroundColor: 'black',
},
'& input:valid:focus + fieldset': {
borderColor: '#ff9800',
borderLeftWidth: 5,
padding: '4px !important', // override inline-style
},
},
})(TextField);
//Style MUI
const useStyles = makeStyles((theme) => ({
root: {
height: '100vh',
backgroundColor: 'black',
},
input: {
color: '#ff9800',
},
formBackground: {
background: 'black',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
},
paper: {
margin: theme.spacing(8, 4),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
avatar: {
margin: theme.spacing(1),
},
form: {
width: '100hv',
height: '100%', // Fix IE 11 issue.
marginTop: theme.spacing(0),
color: '#ff9800',
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
//Label Style
const useLabelStyles = makeStyles({
root: {
color: '#ff9800',
'&.Mui-focused': {
color: '#ff9800',
},
fontSize: '14px',
},
});
-----------
const App = () => {......}
export default App
How can I take the MUI part to the separated file?
In one of my projects I actually used createMuiTheme() in another file then added my own classes under the overrides. Then the entire app is wrapped in the ThemeProvider which you pass in your custom theme. Then you can just give the component a className.
So my MuiSetup.js equivalent file looks something like this:
import { createMuiTheme } from '#material-ui/core';
// Describe material ui theme
export const customTheme = (isDark = false) => {
return createMuiTheme({
palette: isDark
? {
common: {...},
background: {...},
primary: {...},
secondary: {...},
error: {...},
text: {...},
}
: {// Light theme stuff},
overrides: {
MuiAppBar: {...},
MuiButton: {
// Button example...
root: {
'&.CustomButton': {
margin: 0,
},
'&.OtherCustomButton': {
width: '100%',
minHeight: 'inherit',
},
'&$disabled': {
color: grey[600],
},
},
outlinedSecondary: {
'&$disabled': {
border: `1px solid ${grey[600]}`,
},
},
},
// Other mui components...
},
});
};
You'll need to read the API docs to find your inputs.
Then the ThemeProvider:
import React, { Component } from 'react';
import { ThemeProvider } from '#material-ui/core';
import { customTheme } from './MuiSetup.js';
class App extends Component {
render() {
const currentTheme = customTheme(true);
return (
<ThemeProvider theme={currentTheme}>
<h1>My app</h1>
// Usage
<Button className='CustomButton'/>
</ThemeProvider>
);
}
}
export default App;
Then from any component within the app, you can do: <Button className='CustomButton'/> as I've done above.
Not sure if this is the best way, might be a bit tricky to make multiple updates however you could do something similar in your situation and create a function in another file that returns your custom makeStyles.
HTH
Ciao, to put these customizations in a separated file is just necessary to define them in aseparated file with the key "export const ..." and then, when you want to import them, just write:
import { ValidationTextField } from "./muiCustomizations"; // supposing that you defined your customizations in a file called muiCustomizations.js
and then you can use them:
function MyComponent() {
return (
<div>
<ValidationTextField />
</div>
);
}
Here a codesandbox example.
I'm converting a React app to TypeScript, and my app container component contains these container components:
App.tsx
class App extends Component {
render() {
return (
<AppContainer>
<HeaderContainer />
<TabContainer />
<ViewContainer />
</AppContainer>
)
}
}
export default App
HeaderContainer.tsx
const mapStateToProps = (state: AppState) => ({version: state.settings.version})
const mapDispatchToProps = () => ({})
export default connect(
mapStateToProps,
mapDispatchToProps
)(Header)
Header.tsx
const HeaderContainer = styled('header')({
flex: '0 0 auto',
flexDirection: 'row-reverse',
});
const HeaderRow = styled('div')({
padding: '10px 0px 0px 0px',
display: 'flex',
flexDirection: 'row-reverse',
justifyContent: 'right',
maxHeight: '100px',
});
const HeaderLogo = styled('img')({
margin: '0px 5px',
height: '80px',
width: '80px',
display: 'flex',
order: 1,
});
const HeaderInfo = styled('div')({
margin: '0px 15px 0px 10px',
display: 'flex',
justifyContent: 'flex-start',
flexDirection: 'column',
order: 0,
});
const HeaderName = styled('div')({
display: 'flex',
marginTop: '7px',
flexGrow: 1,
order: 0,
fontSize: '40px',
});
const HeaderVersion = styled('div')({
display: 'flex',
flexGrow: 1,
order: 1,
fontSize: '20px',
});
const Header = ({ version }) => (
<HeaderContainer>
<Disclaimer />
<HeaderRow>
<HeaderLogo src={process.env.PUBLIC_URL + "/icons/icon-512x512.png"} alt="logo" />
<HeaderInfo>
<HeaderName>App Name</HeaderName>
<HeaderVersion>{version}</HeaderVersion>
</HeaderInfo>
</HeaderRow>
</HeaderContainer>
)
Header.propTypes = {
version: PropTypes.string.isRequired,
}
export default Header
I'm getting a type error on HeaderContainer in App.tsx:
Type error: Property 'version' is missing in type '{}' but required in type 'Pick<InferProps<{ version: Validator<string>; }>, "version">'.
I do basically understand what this is saying. However, I don't get why the error is showing up on HeaderContainer and not in Header, and I don't get why the error is showing up at all because Header is only being implemented inside of the connect function in the container, passing it the required prop.
Commenting out the propTypes in Header silences the error.
are you using the version prop anywhere inside your styled component?
you might have to pass version directly to the styled component like so const Header = styled.div<HeaderProps>
where HeaderProps is:
interface HeaderProps {
version: type
}
material-ui introduces a way of using classname for styling component. I have a button component shown as below. It uses createStyles and withStyles to inject the styles as classes into the component. But I don't know how to set the focus style of the Button.
import Button from '#material-ui/core/Button';
const styles = createStyles({
button: {
minHeight: '3rem',
lineHeight: '3rem',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-around',
fontSize: '0.8rem',
fontFamily: 'Roboto',
color: '#008091',
border: '2px solid #008091',
borderRadius: '0.375rem',
marginRight: '1rem',
marginTop: '2rem',
marginBottom: '2rem',
minWidth: '180px',
textAlign: 'center',
fontWeight: 700,
margin: '1rem 0',
padding: 0
},
selected: {
backgroundColor: '#008091',
color: 'white'
}
});
interface Props {
classes: { [className in keyof typeof styles]: string };
style?: React.CSSProperties;
text: string;
selected?: boolean;
onClick: (event: React.MouseEvent<HTMLElement>) => void;
}
const TextButton = ({ classes, style, text, onClick, selected }: Props) => {
return (
<Button
className={selected ? classNames(classes.button, classes.selected) : classes.button}
style={style}
onClick={onClick}
>
{text}
</Button>
);
};
Psuedo selectors can be added by:
const styles = createStyles({
button: {
// main styles,
"&:focus": {
color: "red"
}
}
});
This should do the trick
overrides: {
MuiButton: {
root: {
'&:focus': {
color: 'rgba(0, 0, 0, 0.87)',
backgroundColor: 'rgba(0, 0, 0, 0.87)',
},
},
},
},