I just need my text in material UI to shrink with the viewport, but I can only get it to wrap. I've been following the material UI docs but even their examples did not work in my code.
import React, { useState } from 'react';
import { Grid, Typography } from '#material-ui/core';
import { createMuiTheme, responsiveFontSizes, ThemeProvider } from '#material-ui/core/styles';
// import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Search from './Search';
import LanguageFilter from './LanguageFilter';
import RepositoryList from './RepositoryList';
import Sort from './Sort';
let theme = createMuiTheme();
theme = responsiveFontSizes(theme);
const App = () => {
const [results, setResults] = useState([]);
const [filter, setFilter] = useState('');
const [sort, setSort] = useState('default');
return (
<Grid
container
direction="column"
alignContent="center"
alignItems="center"
spacing="3"
>
<Grid item>
<ThemeProvider theme={theme}>
<Typography variant="h1">GitHub Repo Search</Typography>
</ThemeProvider>
</Grid>
<Grid item>
<Search setResults={setResults} sort={sort} />
</Grid>
<Grid item>
<Grid container alignItems="center" justify="center">
<Sort sort={sort} setSort={setSort} />
<LanguageFilter setFilter={setFilter} />
</Grid>
</Grid>
<Grid item>
<RepositoryList results={results} filter={filter} />
</Grid>
</Grid>
);
};
export default App;
This is the component I am working in. I have tried defining the theme both inside and outside of the component, this is just my last attempt before coming here. Not sure how to make it responsive and can't find anything online that works for me.
Solved it. I needed to have my ThemeProvider wrap my entire app, not just where it was needed.
Related
I have a JS file (react) that looks like this:
import { Grid, TextField, makeStyles } from '#material-ui/core'
import React from 'react'
import {useState} from 'react'
//remove this function and refresh. see magic.
const useStyle = makeStyles(theme => ({
root:{
'& .MuiFormControl-root':{
width : '80%',
margin : theme.spacing(.75)
}
}
}))
const initialFormValues = {
id:0,
name:'',
username:'',
email:''
}
export default function EntryForm() {
const [values, setvalues] = useState(initialFormValues)
return (
<form className={useStyle().root}>
<Grid container>
<Grid item>
<TextField
size='small'
variant='outlined'
label='name'
value={values.name} />
<TextField
size='small'
variant='outlined'
label='username'
value={values.username} />
<TextField
size='small'
variant='outlined'
label='email'
value={values.email} />
</Grid>
<Grid item>
</Grid>
</Grid>
</form>
)
}
This works fine, and invoked, the fields get rendered along with it's parent components.
However, changing first line to:
import { Grid, TextField, makeStyles } from '#mui/material'
and refreshing the browser makes the whole page empty. Apparently, this happens specifically with makeStyles from '#mui/material' usage. Using Grid and Textfield from '#mui/material' only doesn't cause that.
What is happening here?
As per docs you should import makeStyles from #mui/styles.
Please try updating the import statement as:
import { makeStyles } from "#mui/styles";
I'm trying to get a Tv show to display on multiple cards. I'm just using one Tv show before I start adding any more. So it should basically display one tv show on all the cards.
The error is coming from the tvList.js.
tvList.js
import React from "react";
import Tv from "../tvCard/";
import Grid from "#material-ui/core/Grid";
const TvList = (props) => {
let tvCards = props.tvshows.map((m) => (
<Grid key={m.id} item xs={12} sm={6} md={4} lg={3} xl={2}>
<Tv key={m.id} tv={m} />
</Grid>
));
return tvCards;
};
export default TvList;
The Tv card seems to be fine and is working in my storybook.
tvCard.js
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import Card from "#material-ui/core/Card";
import CardActions from "#material-ui/core/CardActions";
import CardContent from "#material-ui/core/CardContent";
import CardMedia from "#material-ui/core/CardMedia";
import CardHeader from "#material-ui/core/CardHeader";
import Button from "#material-ui/core/Button";
import Typography from "#material-ui/core/Typography";
import FavoriteIcon from "#material-ui/icons/Favorite";
import CalendarIcon from "#material-ui/icons/CalendarTodayTwoTone";
import StarRateIcon from "#material-ui/icons/StarRate";
import IconButton from "#material-ui/core/IconButton";
import Grid from "#material-ui/core/Grid";
import img from '../../images/tv-poster-placeholder.png'
const useStyles = makeStyles({
card: { maxWidth: 345 },
media: { height: 500 },
avatar: {
backgroundColor: "rgb(255, 0, 0)",
},
});
export default function TvCard(props) {
const classes = useStyles();
const tv = props.tv;
return (
<Card className={classes.card}>
<CardHeader className={classes.header} title={tv.name} />
<CardMedia
className={classes.media}
image={
tv.poster_path
? `https://image.tmdb.org/t/p/w500/${tv.poster_path}`
: img
}
/>
<CardContent>
<Grid container>
<Grid item xs={6}>
<Typography variant="h6" component="p">
<CalendarIcon fontSize="small" />
{tv.first_air_date}
</Typography>
</Grid>
<Grid item xs={6}>
<Typography variant="h6" component="p">
<StarRateIcon fontSize="small" />
{" "} {tv.vote_average}{" "}
</Typography>
</Grid>
</Grid>
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites" onClick={null}>
<FavoriteIcon color="primary" fontSize="large" />
</IconButton>
<Button variant="outlined" size="medium" color="primary">
More Info ...
</Button>
</CardActions>
</Card>
);
}
tvPage.js
import React from "react";
import Header from "../components/headerTvList";
import FilterCard from "../components/filterTvCard";
import Grid from "#material-ui/core/Grid";
import { makeStyles } from "#material-ui/core/styles";
import TvList from "../components/tvList";
const useStyles = makeStyles({
root: {
padding: "20px",
},
});
const TvListPage = (props) => {
const classes = useStyles();
const tvshows = props.tvshows;
return (
<Grid container className={classes.root}>
<Grid item xs={12}>
<Header title={"Discover Tv Shows"} />
</Grid>
<Grid item container spacing={5}>
<Grid key="find" item xs={12} sm={6} md={4} lg={3} xl={2}>
<FilterCard />
</Grid>
<TvList tvshows={tvshows}></TvList>
</Grid>
</Grid>
);
};
export default TvListPage;
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import TvPage from "./pages/tvPage";
const sample = {
"backdrop_path": "/wAEWZm2pSopAbqE5dQWE0ET8aR5.jpg",
"first_air_date": "2021-01-08",
"genre_ids": [
10759,
10765,
99
],
"id": 114695,
"name": "Marvel Studios: Legends",
"origin_country": [
"US"
],
"original_language": "en",
"original_name": "Marvel Studios: Legends",
"overview": "Revisit the epic heroes, villains and moments from across the MCU in preparation for the stories still to come. Each dynamic segment feeds directly into the upcoming series — setting the stage for future events. This series weaves together the many threads that constitute the unparalleled Marvel Cinematic Universe.",
"popularity": 140.788,
"poster_path": "/EpDuYIK81YtCUT3gH2JDpyj8Qk.jpg",
"vote_average": 7.6,
"vote_count": 515
}
const tvshows = [sample, sample, sample, sample, sample, sample, sample];
const App = () => {
return (
<TvPage tvshows={tvshows} />
);
};
ReactDOM.render(<App />, document.getElementById("root"));
It could be an with the tvPage component.
const tvshows = props.tvshows;
Here the value of tvshows might be empty initially. I'd suggest i your TvList component do an null check.
const TvList = (props) => {
let tvCards = props.tvshows?.map((m) => (
<Grid key={m.id} item xs={12} sm={6} md={4} lg={3} xl={2}>
<Tv key={m.id} tv={m} />
</Grid>
));
return tvCards;
};
The ? is an [optional chaining] operator(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining).
The error is that for some reason, props.tvshows is undefined within TvList, even though it should be passed down from App to TvPage to TvList. At some point down the line, tvshows is lost.
I would suggest logging all instances of tvshows, starting from when it is first defined in App, then logging in TvPage, then TvList, to see where exactly the problem is coming from. Once you've found it, do some debugging to make the problem stop.
I'm new to React and I've been trying to redirect to a different component after getting a response from my API.
I've tried using history, location, and Redirect, but the redirect never happens.
Also, I get undefined when using all of the above.
I'm not sure if this is because my App is defined outside the Router, if it is the reason I'm still unable to fix the issue.
Here is my code:
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AppProvider } from './Context'
import { BrowserRouter as Router } from 'react-router-dom'
ReactDOM.render(
<React.StrictMode>
<AppProvider>
<App />
</AppProvider>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React from 'react';
import './App.css';
import {Home, JoinRoom, CreateRoom, Room } from './pages';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
function App() {
return (
<div className="App">
<Router>
<Switch>
<Route path="/" exact={true}>
<Home />
</Route>
<Route path="/join">
<JoinRoom />
</Route>
<Route path="/create">
<CreateRoom />
</Route>
<Route path="/room/:roomCode">
<Room />
</Route>
</Switch>
</Router>
</div>
);
}
export default App;
Context.js
Here, in the handleRoomButtonPressed, I'm getting data from the API and trying to redirect.
import React, { useState, useContext } from 'react';
import axios from 'axios';
import { Redirect, useHistory } from "react-router-dom";
const AppContext = React.createContext()
const AppProvider = ({ children }) => {
// const history = useHistory();
const [guestCanPause, setGuestCanPause] = useState(true);
const [votesToSkip, setVotesToSkip] = useState(2);
const [isHost, setIsHost] = useState(false);
const handleVotesChange = (e) => {
e.preventDefault();
setVotesToSkip(e.target.value);
}
const handleGuestCanPauseChange = (e) => {
e.preventDefault();
setGuestCanPause(e.target.value)
}
const handleRoomButtonPressed = async (props) => {
const roomData = { guest_can_pause: guestCanPause, votes_to_skip: votesToSkip };
const response = await axios.post('/api/create-room/', roomData);
console.log(response.data)
const redirectUrl = "/room/" + response.data.code;
console.log(props)
return <Redirect to={redirectUrl} />
}
const getRoomDetails = async (roomCode) => {
axios
.get("/api/get-room?code=" + roomCode)
.then((res) => {
console.log(res.data)
setVotesToSkip(res.data.votes_to_skip);
setGuestCanPause(res.data.guest_can_pause);
setIsHost(res.data.is_host);
})
.catch((err) => console.log(err));
}
return <AppContext.Provider value={{ guestCanPause,
votesToSkip,
isHost,
handleGuestCanPauseChange,
handleVotesChange,
handleRoomButtonPressed,
getRoomDetails, }}>
{children}
</AppContext.Provider>
}
export const useGlobalContext = () => {
return useContext(AppContext)
}
export { AppContext, AppProvider }
The onClick is called in CreateRoom.js
import React, { useState, } from 'react';
import { useGlobalContext } from '../Context'
import { Link } from 'react-router-dom';
import { Button, Grid, Typography, TextField, FormHelperText, FormControl, Radio, RadioGroup, FormControlLabel } from '#material-ui/core'
function CreateRoom() {
const defaultVotes = 2;
const { handleGuestCanPauseChange, handleVotesChange, handleRoomButtonPressed } = useGlobalContext();
return (
<Grid container spacing={1}>
<Grid item xs={12} align="center">
<Typography component="h4" variant="h4">
Create A Room
</Typography>
</Grid>
<Grid item xs={12} align="center">
<FormControl component="fieldset">
<FormHelperText>
<div align="center">Guest Control of Playback state</div>
</FormHelperText>
<RadioGroup row defaultValue="true" onChange={handleGuestCanPauseChange}>
<FormControlLabel value="true"
control={<Radio color="primary" />}
label="Play/Pause" labelPlacemment="bottom" />
<FormControlLabel value="false"
control={<Radio color="secondary" />}
label="No Control" labelPlacemment="bottom" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<FormControl>
<TextField required={true}
type="number" onChange={handleVotesChange}
defaultValue={defaultVotes}
inputProps={{ min: 1,
style: { textAlign: "center" },
}}
/>
<FormHelperText>
<div align="center">Votes Required To Skip Song</div>
</FormHelperText>
</FormControl>
</Grid>
<Grid item xs={12} align="center">
<Button
color="primary"
variant="contained"
onClick={handleRoomButtonPressed}
>
Create A Room
</Button>
</Grid>
<Grid item xs={12} align="center">
<Button color="secondary" variant="contained" to="/" component={Link}>
Back
</Button>
</Grid>
</Grid>
)
}
export default CreateRoom
If I understood a subject correctly your AppProvider is located above the router in the component tree. Thus, the react router cannot inject its dependencies into your AppProvider. If you want to access react-router API, such as useHistory hook or others, you should call it from one of the Router children, then it will works.
When I run the code below I expected that styles would be injected inside props, but I always end up getting undefined props.
I am not providing any props to this component.
const styles = theme => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1),
width: '25ch',
},
},
});
class Editor extends React.Component {
render() {
const { classes } = this.props;
return (
<div className="editor">
<form className={classes.root} noValidate autoComplete="off">
<TextField id="standard-basic" label="Standard" />
<Button variant="contained" color="primary">
Hello World
</Button>
</form>
</div>
);
}
}
export default withStyles(styles)(Editor);
Hi you can use makeStyles from '#material-ui/core/styles'. Please check complete example below:
import React from 'react';
import {makeStyles} from '#material-ui/core/styles';
import Grid from '#material-ui/core/Grid';
import FormLabel from '#material-ui/core/FormLabel';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import RadioGroup from '#material-ui/core/RadioGroup';
import Radio from '#material-ui/core/Radio';
import Paper from '#material-ui/core/Paper';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
paper: {
height: 140,
width: 100,
},
control: {
padding: theme.spacing(2),
},
}));
export default function SpacingGrid() {
const [spacing, setSpacing] = React.useState(2);
const classes = useStyles();
const handleChange = (event) => {
setSpacing(Number(event.target.value));
};
return (
<Grid container className={classes.root} spacing={2}>
<Grid item xs={12}>
<Grid container justify="center" spacing={spacing}>
{[0, 1, 2].map((value) => (
<Grid key={value} item>
<Paper className={classes.paper}/>
</Grid>
))}
</Grid>
</Grid>
<Grid item xs={12}>
<Paper className={classes.control}>
<Grid container>
<Grid item>
<FormLabel>spacing</FormLabel>
<RadioGroup
name="spacing"
aria-label="spacing"
value={spacing.toString()}
onChange={handleChange}
row
>
{[0, 1, 2].map((value) => (
<FormControlLabel
key={value}
value={value.toString()}
control={<Radio/>}
label={value.toString()}
/>
))}
</RadioGroup>
</Grid>
</Grid>
</Paper>
</Grid>
</Grid>
);
}
Source
I have a website built with Gatsby.js using the Material-UI.
Specific problem is this: I want to use the Google Tag Manager "Element Visibility" triggers. If some HTML element becomes visible, GTM should fire some GA tag.
Question is this: how can I specify the HTML ID for a material-ui component for GTM (or anything else) to find it?
First example:
// ...react imports omitted...
import makeStyles from '#material-ui/core/styles/makeStyles';
import Box from '#material-ui/core/Box';
import Grid from '#material-ui/core/Grid';
import CloseIcon from '#material-ui/icons/Close';
import Link from '~components/Link';
import ButtonSubmit from '~components/form-buttons/ButtonSubmit';
import Container from '~components/Container';
// ... all other imports are in-house code
const useStyles = makeStyles(theme => ({ /* ...styles... */}));
const GuestUserSoftSaleSecondPopup = ({ which, ...rest }) => {
const classes = useStyles();
// ...setup code omitted...
return (
<Box bgcolor="#474d5c" width="100%" py={4} className={classes.banner}>
<Container>
<Grid container direction="row" justify="space-between" alignItems="center" spacing={2}>
<Grid item xs={12} sm={1} md={3} lg={4}>
<CloseIcon onClick={handleClose} size="large" className={classes.closeIcon} />
</Grid>
<Grid item xs={12} sm={7} md={5} lg={4}>
<Link to="/subscribe" variant="h5" className={classes.linkStyle}>
Become a member for full access
</Link>
</Grid>
<Grid item xs={12} sm={4} className={classes.buttonPosition}>
<Link to="/subscribe" underline="none" className={classes.linkStyle}>
<ButtonSubmit type="button" fullWidth={false}>
See my option
</ButtonSubmit>
</Link>
</Grid>
</Grid>
</Container>
</Box>
);
};
// ...proptypes and `export` clause
Second example:
// ...react imports omitted...
import makeStyles from '#material-ui/core/styles/makeStyles';
import MuiDialog from '#material-ui/core/Dialog';
const useStyles = makeStyles(() => ({ /* ...styles... */ }));
const Dialog = ({ children, background, backdrop, isOpen, ...rest }) => {
const classes = useStyles({ background });
return (
<MuiDialog
open={isOpen}
maxWidth="sm"
fullWidth
disableBackdropClick
disableEscapeKeyDown
BackdropProps={{
className: backdrop ? classes.backdropBM : classes.backdrop
}}
PaperProps={{
className: classes.paper
}}
scroll="body"
{...rest}
>
{children}
</MuiDialog>
);
};
If you look at the API documentation for almost any of the Material-UI components, you will find at the end of the "Props" section a statement like the following example from Dialog:
Any other props supplied will be provided to the root element (Modal).
This means that any props not explicitly recognized by this component will be passed along eventually to whatever HTML element is the outermost element rendered. So for most Material-UI components, in order to add an id property, you just specify it.
My example below (a modification of the Simple Dialog demo) includes three different ids: one on the Dialog element which will be placed on the outermost div of the Modal, one specified via the PaperProps which will go on the main Paper div of the visible content of the dialog, and one on the Box wrapped around the dialog content.
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Avatar from "#material-ui/core/Avatar";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemAvatar from "#material-ui/core/ListItemAvatar";
import ListItemText from "#material-ui/core/ListItemText";
import DialogTitle from "#material-ui/core/DialogTitle";
import Dialog from "#material-ui/core/Dialog";
import PersonIcon from "#material-ui/icons/Person";
import Typography from "#material-ui/core/Typography";
import { blue } from "#material-ui/core/colors";
import Box from "#material-ui/core/Box";
const emails = ["username#gmail.com", "user02#gmail.com"];
const useStyles = makeStyles({
avatar: {
backgroundColor: blue[100],
color: blue[600]
}
});
function SimpleDialog(props) {
const classes = useStyles();
const { onClose, selectedValue, open } = props;
const handleClose = () => {
onClose(selectedValue);
};
const handleListItemClick = value => {
onClose(value);
};
return (
<Dialog
onClose={handleClose}
aria-labelledby="simple-dialog-title"
open={open}
PaperProps={{ id: "MyDialogPaperID" }}
id="ThisIDWillBeOnTheModal"
>
<DialogTitle id="simple-dialog-title">Set backup account</DialogTitle>
<Box id="MyBoxID">
<List>
{emails.map(email => (
<ListItem
button
onClick={() => handleListItemClick(email)}
key={email}
>
<ListItemAvatar>
<Avatar className={classes.avatar}>
<PersonIcon />
</Avatar>
</ListItemAvatar>
<ListItemText primary={email} />
</ListItem>
))}
</List>
</Box>
</Dialog>
);
}
SimpleDialog.propTypes = {
onClose: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
selectedValue: PropTypes.string.isRequired
};
export default function SimpleDialogDemo() {
const [open, setOpen] = React.useState(false);
const [selectedValue, setSelectedValue] = React.useState(emails[1]);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = value => {
setOpen(false);
setSelectedValue(value);
};
return (
<div>
<Typography variant="subtitle1">Selected: {selectedValue}</Typography>
<br />
<Button variant="outlined" color="primary" onClick={handleClickOpen}>
Open simple dialog
</Button>
<SimpleDialog
selectedValue={selectedValue}
open={open}
onClose={handleClose}
/>
</div>
);
}
Material UI components don't let you set an id for them since the implementation inside should be a black box and may contain multiple html element. See if you can wrap the element in a div and put the id on that instead.
Another option would be to add a class (via the classes prop) to the element instead but I'm not sure if Google Tag Manager can use those instead of ids.