Material-UI and React IE11 compatibility - reactjs

I'm trying to implement material-ui in my React application. Unfortunately we still have to support IE, but anything material-ui I try to add makes the app not display, and with loads of undefined errors in the console. For example, I'm trying to add the Tabs component code directly from material-ui's site (using v4.8.3):
import React from 'react';
import PropTypes from 'prop-types';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<Typography
component="div"
role="tabpanel"
hidden={value !== index}
id={`nav-tabpanel-${index}`}
aria-labelledby={`nav-tab-${index}`}
{...other}
>
{value === index && <Box p={3}>{children}</Box>}
</Typography>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.any.isRequired,
value: PropTypes.any.isRequired,
};
function a11yProps(index) {
return {
id: `nav-tab-${index}`,
'aria-controls': `nav-tabpanel-${index}`,
};
}
function LinkTab(props) {
return (
<Tab
component="a"
onClick={event => {
event.preventDefault();
}}
{...props}
/>
);
}
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
backgroundColor: theme.palette.background.paper,
},
}));
export default function Header() {
const classes = useStyles();
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs
variant="fullWidth"
value={value}
onChange={handleChange}
aria-label="nav tabs example"
>
<LinkTab label="Page One" href="/drafts" {...a11yProps(0)} />
<LinkTab label="Page Two" href="/trash" {...a11yProps(1)} />
<LinkTab label="Page Three" href="/spam" {...a11yProps(2)} />
</Tabs>
</AppBar>
<TabPanel value={value} index={0}>
Page One
</TabPanel>
<TabPanel value={value} index={1}>
Page Two
</TabPanel>
<TabPanel value={value} index={2}>
Page Three
</TabPanel>
</div>
);
}
but all I get in IE is many of these:
Is there something I am missing here? Any polyfills I need to add? Chrome displays just fine with no errors. Any help is much appreciated.

From the Supported Platform of Material-UI, it says it also supports Internet Explorer 11. You don't need to provide any JavaScript polyfill as it manages unsupported browser features internally and in isolation.
I made a demo using Tab component and the code you providing, it works well in IE 11. Maybe the error occurs in other codes and you could check again where the error points to. Besides, have you had the right package.json? I use package.json like below:
{
"title": "Material demo",
"devDependencies": {
"react-scripts": "latest"
},
"description": "https://github.com/mui-org/material-ui/blob/master/docs/src/pages/components/tabs/SimpleTabs.js",
"dependencies": {
"#material-ui/core": "latest",
"#types/react": "^16.8.6",
"prop-types": "latest",
"react": "latest",
"react-dom": "latest"
}
}
You could refer to my online demo. If the issue still persists, you could provide a reproducible demo using online code editor like StackBiltz. I think that will help us have a better understanding of the issue and see how to help.

Related

Material UI Tabs with routes

So I'm using Material UI Tabs, and I'm trying when a user taps on tab it redirects him to a tab with a link but it would be still in the same main page
Example of what i'm looking for please notice the link changes as the tabs changes:
Material UI VerticalTabs
import * as React from 'react';
import PropTypes from 'prop-types';
import Tabs from '#mui/material/Tabs';
import Tab from '#mui/material/Tab';
import Typography from '#mui/material/Typography';
import Box from '#mui/material/Box';
function TabPanel(props) {
const { children, value, index, ...other } = props;
return (
<div
role="tabpanel"
hidden={value !== index}
id={`vertical-tabpanel-${index}`}
aria-labelledby={`vertical-tab-${index}`}
{...other}
>
{value === index && (
<Box sx={{ p: 3 }}>
<Typography>{children}</Typography>
</Box>
)}
</div>
);
}
TabPanel.propTypes = {
children: PropTypes.node,
index: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
};
function a11yProps(index) {
return {
id: `vertical-tab-${index}`,
'aria-controls': `vertical-tabpanel-${index}`,
};
}
export default function VerticalTabs() {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Box
sx={{ flexGrow: 1, bgcolor: 'background.paper', display: 'flex', height: 224 }}
>
<Tabs
orientation="vertical"
variant="scrollable"
value={value}
onChange={handleChange}
aria-label="Vertical tabs example"
sx={{ borderRight: 1, borderColor: 'divider' }}
>
<Tab label="Item One" {...a11yProps(0)} />
<Tab label="Item Two" {...a11yProps(1)} />
<Tab label="Item Three" {...a11yProps(2)} />
</Tabs>
<TabPanel value={value} index={0}>
Item One
</TabPanel>
<TabPanel value={value} index={1}>
Item Two
</TabPanel>
<TabPanel value={value} index={2}>
Item Three
</TabPanel>
</Box>
);
}
I know that I have to use react-router-dom but I didn't know how to implement, any help would be appreciated.
Create a route component or destructive the routes in App.js. Just install react-router-dom and use BrowserRouter as Route as the Parent tag. Then add routes and structure your routes for your pages there.
import {BrowserRouter as Router, Routes, Route} from 'react-route-dom'
<Router>
<Routes>
<Route path='/pageName' element={<pageName/>}>
<Route path='/pageName2' element={<pageName2/>}>
</Routes>
</Router>
Also refer react router v6 docs. This playlist would helpful to using different routing mechanism. In your case you need to watch the tutorial 8 to do the nested routes. If you're a person who familiar with v5 and want to know the changes in v6, then refer this video also.

Material UI custom styles do not apply on the component

I have just started learning Material UI in react js.
When it comes to custom styling, I encounter an issue where everything seems to be logically correct but the styling doesn't apply.
There is a button in Create.js component which I'm trying to change its style with the help of import { makeStyles } from "#material-ui/styles"
These are the dependencies installed in package.json:
"dependencies": {
"#emotion/react": "^11.4.1",
"#emotion/styled": "^11.3.0",
"#material-ui/styles": "^4.11.4",
"#mui/icons-material": "^5.0.1",
"#mui/material": "^5.0.2",
Here is Create.js component:
import React from "react";
import Typography from "#mui/material/Typography";
import { Button, Container } from "#mui/material";
import KeyboardArrowRightIcon from "#mui/icons-material/KeyboardArrowRight";
import { makeStyles } from "#material-ui/styles";
const useStyle = makeStyles({
btn: {
fontSize: 60,
backgroundColor: "red",
},
});
export default function Create() {
const classes = useStyle();
return (
<Container>
<Typography
variant="h6"
color="textSecondary"
align="center"
gutterBottom
>
Create a New Note
</Typography>
<Button
className={classes.btn}
onClick={() => console.log("you clicked me")}
type="submit"
color="secondary"
variant="contained"
endIcon={<KeyboardArrowRightIcon />}
>
Submit
</Button>
</Container>
);
}
This is what I see when I inspect the element:
DevTools keeps ignoring this style
The code is so far good. One difference I observed in documentation and the provided code is the import of makeStyles. Try importing makesStyles from #mui/styles. For reference have a look at documentation.
Also MUI is now compatible with plain css for minor tweaks like these. Please refer here
As far as the issue of browser ignoring styles is concerned a workaround can be using important keyword in custom styles but that is not a recommended and inefficient way.

Tabs that load before the internal tabs causes an error "The value provided to the Tabs component is invalid"

I use a tabs element of material-UI, but the tabs itself I receive from props.
the first tab with the value 0, I want to be chosen.
Here my code:
import React, { useContext } from 'react';
import { ChannelContext } from '../context/channelContext'
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
export default function Channels(props) {
let channelsList = []
props.channels.forEach((elem,i) => {
channelsList.push( <Tab key={i} label={elem.channel_name} value={elem.server_id-1} />)
})
const [, setchannelBanner] = useContext(ChannelContext)
const [channelId, setChannelId] = React.useState(0);
const handleChange = (event, newValue) => {
setChannelId(newValue)
setchannelBanner(props.channels[newValue].bg_image);
};
return (
<AppBar position="static" color="inherit" elevation={0}>
<Tabs
value={channelId}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs" className="tabs">
{channelsList}
</Tabs>
</AppBar>
);
}
I receive this error on the browser console:
"Material-UI: The value provided to the Tabs component is invalid.
None of the Tabs' children match with `0`.
You can provide one of the following values: NaN."
if i change the React.useState(0) to React.useState(false) or React.useState(NaN), the error disappear but the first tab not choosen.
I understand that it's happening because the main Tabs element loads before the internal tabs array.
How do you suggest resolving it?
One way of solving it would be to use useEffect and change the state from NaN to 0 when the main tabs are loaded.
It would be better if you can share a link to code sandbox
I found the answer is to use useEffect on props and then inside the useEffect check if the first element/channel exists, only then make the setChannelId(0).
import React, { useContext, useEffect } from 'react';
import { ChannelContext } from '../context/channelContext'
import AppBar from '#material-ui/core/AppBar';
import Tabs from '#material-ui/core/Tabs';
import Tab from '#material-ui/core/Tab';
export default function Channels(props) {
const [, setchannelBanner] = useContext(ChannelContext)
const [channelId, setChannelId] = React.useState(false);
// setChannelId after tabs are loaded
useEffect(() => {
if( props.channels[0].server_id >= 0 ) setChannelId(0)
}, [props]);
const handleChange = (event, newValue) => {
setChannelId(newValue)
setchannelBanner(props.channels[newValue].bg_image);
};
return (
<AppBar position="static" color="inherit" elevation={0}>
<Tabs
value={channelId}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs" className="tabs">
{props.channels.map((elem,i) => (
<Tab key={i} label={elem.channel_name} value={elem.server_id-1}/>
))}
</Tabs>
</AppBar>
);
}

Making a Materal UI card template in react

I'm trying to make a card template in React using Material UI. I need this template so I can easily generate more cards in the future. The documentation often have the script in one single file, and never explain how to separate it and still having it to work. I don't gonna use the export import lines here just to simplify the code.
Lets say i have a Cardtemplate.jsx
const useStyles = makeStyles({
root: {
maxWidth: 345,
},
subtitle: {
fontSize: 14,
},
card:{
height: 100,
}
title:{
fontSize: 12,
}
});
const Cardtemplate = ({ classes, image, title, subtitle }) => {
return (
<Card className={classes.card}>
<CardMedia image={image} />
<Typography className={classes.title} variant={'h2'}>{title}</Typography>
<Typography className={classes.subtitle}>{subtitle}</Typography>
</Card>
);
};
export default Cardemplate;
Then I want to use the temple to generate card1.jsx
export default function Card1() {
const classes = useStyles();
return (
<Cardtemplate
classes={styles}
title={'Some title'}
subtitle={'some subtitle'}
image={'image.png'}
/>
);
}
Finally in the App.js I would render all the cards like this.
function App(){
return(
<Card1 />
<Card2 />
<Card3 />
...
...
)
};
The problem is that I getting syntaxError telling that Identifier 'useStyles' has already been declared or some undefined properties is missing. I have tried to place the const classes = useStyles(); in the cardTemplate.jsx.
I have also tried to wrap the const useStyles inside the const Cardtemplate function just to make sure that the useStyles also get exported, but all I get Is errors.
There's something I have been missing here. How can I do this the proper way without any further errors?
Your CardTemplate shouldn't receive classes from props, it should be declared on it, using the useStyles defined above, so it should look like this:
const useStyles = makeStyles({
// style
});
export default function CardTemplate({ title, subtitle }) {
const classes = useStyles();
return (
<Card className={classes.card}>
<Typography className={classes.title} variant={"h2"}>
{title}
</Typography>
<Typography className={classes.subtitle}>{subtitle}</Typography>
</Card>
);
}
Then, you can have each of your Cards importing this component and reusing, per example:
// Card1.js
export default () => {
return <CardTemplate title="Card 1" subtitle="The First" />;
};
// Card2.js
export default () => {
return <CardTemplate title="Card 2" subtitle="The Second" />;
};
// Card3.js
export default () => {
return <CardTemplate title="Card 3" subtitle="The Third" />;
};
Lastly, you can render them in any place as you seem fit, example:
ReactDOM.render(
<>
<Card1 />
<Card2 />
<Card3 />
</>,
document.querySelector("#root")
);
There is a running code sandbox I made for you. You can check it here!

Material-ui svg-icons inside another element doesnt align to the center

I'm using the material ui library in my react project, and I have come across a strange issue, when I try to use svg icons inside a button-icon, the icom doesn't align to the center.
for example:
<ListItem key={product.id}
primaryText={product.title}
leftAvatar={<Avatar src={product.img}/>}
rightIcon={<IconButton><RemoveIcon/></IconButton>}/>
for this code I will get the following result:
And for this code:
<ListItem key={product.id}
primaryText={product.title}
leftAvatar={<Avatar src={product.img}/>}
rightIcon={<RemoveIcon/>}/>
I will get the following result :
My question is, how do i get to the result of my second example, but that the icon will we inside another element?
This is kind of late but I recently had the same issue and solved it by wrapping the IconButton component in a custom component and extending the css. You may have to change some other CSS to make it align perfectly but this worked for my use case.
import React, { PropTypes, Component } from 'react';
import IconButton from 'material-ui/IconButton';
const CustomIconButton = (props) => {
const { style } = props;
const additionalStyles = {
marginTop: '0'
};
return(
<IconButton {...props } style={{ ...style, ...additionalStyles }} iconStyle={{ fontSize: '20px' }}/>
);
};
CustomIconButton.PropTypes = {
// listed all the props that IconButton requires (check docs)
};
export default PPIconButton;
This is what a simplified usage of this custom IconButton looks like:
const deleteIconButton = (deleteFunc) => {
return <CustomIconButton
touch={true}
tooltip="Delete"
tooltipPosition="top-right"
onTouchTap={deleteFeed}
iconClassName="fa fa-trash"
/>;
};
class MyList extends Component {
render() {
return (
<div>
<List>
<ListItem value={ i } primaryText="My List Item" rightIcon={ deleteIconButton(() => this.props.deleteFeed(i) } />
) }
</List>
</div>
);
}
}
Passing the styles down to the inner element worked for me:
return <SvgIcon style={this.props.style} />
check this code, working fine for me
import React from 'react';
import List from 'material-ui/List';
import ListItem from 'material-ui/List/ListItem';
import Delete from 'material-ui/svg-icons/action/delete';
const MenuExampleIcons = () => (
<div>
<List style={{width:"300px"}}>
<ListItem primaryText="New Config" leftIcon={<Delete />} />
<ListItem primaryText="New Config" rightIcon={<Delete />} />
</List>
</div>
);
export default MenuExampleIcons;

Resources