I am building a React App and want to navigate to another page by "clicking on the card" to display information about that place.
This is my App.js
import React from "react";
// import NavBar from "./NavBar";
import DestCard from "./Card";
import { Grid } from "#mui/material";
// import MidPage from "./Midpage";
const cardInfo = [
{
image: "https://cdn.myanimelist.net/images/characters/11/174517.jpg",
title: "Lebron James",
text: "THE GOAT",
},
{
image: "https://upload.wikimedia.org/wikipedia/commons/a/aa/TechCrunch_Disrupt_2019_%2848834853256%29_%281%29.jpg",
title: "Stephen Curry",
text: "3 pointer GOD",
},
{
image: "https://upload.wikimedia.org/wikipedia/commons/b/bf/LeBron_James_-_51959723161_%28cropped%29.jpg",
title: "Lebron James",
text: "THE GOAT",
},
{
image: "https://upload.wikimedia.org/wikipedia/commons/a/aa/TechCrunch_Disrupt_2019_%2848834853256%29_%281%29.jpg",
title: "Stephen Curry",
text: "3 pointer GOD",
}
];
function App() {
return (
<div>
<Grid container spacing={{ xs: 1}} columns={{ xs: 4, md:12}}>
{cardInfo.map((details, index) => (
<Grid item xs={2} sm={4} md={4} key={index}>
<DestCard details= {details} />
</Grid>
))}
</Grid>
</div>
)
}
export default App;
Here I have the Card in the grid.
Next is the Card.js
import * as React from 'react';
import {
StyledCard,
CardImage,
CardTextWrapper,
CardTitle
} from './Card.style.js';
function DestCard({ details }) {
// console.log(details.image);
return (
<StyledCard>
<CardImage bg= {details.image}>
<CardTextWrapper>
<CardTitle>
<h2>{details.title}</h2>
<h3>{details.title}</h3>
</CardTitle>
</CardTextWrapper>
</CardImage>
</StyledCard>
)
}
export default DestCard;
I want to know how can I route by clicking the card to another page.
The best bet is just to make the card a styled button component and then use useNavigate() to change pages from there.
import * as React from 'react';
import {
StyledCard,
CardImage,
CardTextWrapper,
CardTitle
} from './Card.style.js';
import { useNavigate } from 'react-router-dom';
function DestCard({ details }) {
const navigate = useNavigate();
// console.log(details.image);
return (
<button classname="styledButton" onClick={() => navigate('/route')}>
<CardImage bg= {details.image}>
<CardTextWrapper>
<CardTitle>
<h2>{details.title}</h2>
<h3>{details.title}</h3>
</CardTitle>
</CardTextWrapper>
</CardImage>
</button>
)
}
export default DestCard;
You can add whatever styles are needed to the card to make it obvious to the user that it's clickable.
Related
I have a react-virtualized List, each item in the rows have an expand button, when the button is clicked the height of the row changes and it expands and show more info.
the issue here is, after the button got clicked the inner Grid (ReactVirtualized__Grid__innerScrollContainer) CSS height property is not updating or the Gid is not re-rendering/notified of the changes in row height
What should I do to make it rerender the row everytime the row item height gets updated?
here is my main index.js file:
import React, {
cloneElement,
Component,
useMemo,
useCallback,
useEffect,
createRef,
useRef,
useState
} from "react";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import {
List,
AutoSizer,
CellMeasurer,
CellMeasurerCache
} from "react-virtualized";
import Expander from "./expander.js";
export default function Test() {
// List data as an array of strings
let list = [
"Brian Vaughn",
"Bob Smith",
"Someone Else",
"I hate making up names"
// And so on...
];
const listRef = useRef();
const expander = (
<Expander onChanged={() => listRef.current?.list?.forceUpdateGrid()} />
);
return (
<div>
<AutoSizer
// disableWidth
// disableHeight
>
{({ width, height }) => {
return (
<List
ref={listRef}
className="List"
autoHeight
width={800}
height={400}
rowCount={list.length}
rowHeight={30}
rowRenderer={({ index, isScrolling, key, style }) => (
<div className="Row" key={key} style={style}>
{list[index]}
{expander}
</div>
)}
/>
);
}}
</AutoSizer>
</div>
);
}
Here is my expander.js file ( Row Component ):
import React, {
cloneElement,
Component,
useMemo,
useCallback,
useEffect,
createRef,
useRef,
useState
} from "react";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
export default function Expander(props) {
// State : Declare
const [isClicked, setIsClicked] = useState(false);
const renderMoreText = () => {
if (isClicked) {
return (
<div style={{ backgroundColor: "red", height: 200 }}>
long lorem ipsum{" "}
</div>
);
}
return null;
};
useEffect(() => {
props.onChanged();
}, [isClicked]);
return (
<div>
<button
onClick={() => {
setIsClicked(true);
}}
>
{" "}
Expand more text{" "}
</button>
{renderMoreText()}
</div>
);
}
Here is my sandbox:
https://codesandbox.io/s/agitated-pond-xq1csq?file=/pages/index.js
I'm not familiar with react-virtualized, but reading the docs you could make use of the List onChanged event together with Expanders recomputeRowHeights().
Updated sandbox (quick and dirty): https://codesandbox.io/s/charming-cookies-omi14k?file=/pages/index.js
Here it's written header.js code and it's testing with jest framework. While im testing im getting the error property of match of props.value as it is undefined. When i compile , compiler shows this link to solve error but its not getting.
[Link:]https://reactjs.org/link/error-boundaries
Header.js
import React from "react";
import { AppBar, Typography, Toolbar, Tabs, Tab, Button } from "#mui/material";
import {IconButton} from "#mui/material";
import { withStyles } from "#material-ui/core/styles";
import {Logout} from '#mui/icons-material'
import "./Header.css";
import Profile from '../Profile/Profile';
import Wallet from '../Wallet/Wallet';
import Market from "../Market/Market";
import Portfolio from "../Portfolio/Portfolio";
import Trade from "../Trade/Trade";
const Header = (props) => {
const { match, history } = props.value;
const { params } = match;
const { page } = params;
console.log(page);
const IndexToTableName = {
0:"Profile",
1:"Walllet",
2:"Trade",
3:"Market",
4:"Portfolio"
};
const TabNametoIndex = {
Profile: 0,
Wallet: 1,
Trade:2,
Market:3,
Portfolio:4
};
let[value,setValue]=React.useState(TabNametoIndex[page]);
const handleChange=(event,newValue)=>{
history.push(`/UserHome/${IndexToTableName[newValue]}`);
setValue(newValue);
};
const WhiteTextTypography = withStyles({
root: {
color: "#25316D"
}
})(Typography);
return (
<div id="Header" style={{height:"100vh"}} data-testid="header">
<AppBar style={{ background: "#7FBCD2", marginleft: "auto", fontFamily:"sans-serif" }} position="sticky">
<Toolbar>
<WhiteTextTypography variant="h5" id="Typo">TradeHut</WhiteTextTypography>
<Tabs id="Tabs" textColor="white" indicatorColor="primary" value={value} onChange={handleChange}>
<Tab label="Profile" className="tab"/>
<Tab label="Wallet" className="tab"/>
<Tab label="Trade" className="tab"/>
<Tab label="Market" className="tab"/>
<Tab label="Portfolio" className="tab"/>
</Tabs>
<div id="IconButton">
<IconButton color="primary" aria-label="add an alarm" size="large" id="logout">
<Logout/>
</IconButton>
</div>
</Toolbar>
</AppBar>
{value===0 && <Profile/>}
{value===1 && <Wallet/>}
{value===2 && <Trade/>}
{value===3 && <Market/>}
{value===4 && <Portfolio/>}
</div>
)
}
export default Header;
Header.test.js
import React from 'react';
import Header from "./Header";
import { render, screen } from '#testing-library/react';
import '#testing-library/jest-dom'
describe("Test the Header Component", () => {
test("To Check whether Header Div is present or not", () => {
render(<Header />);
const buttonList = screen.getByTestId("header");
expect(buttonList).toBeTruthy();
});
});
const Header = (props) => {
const { match, history } = props.value;
...
}
In your test you call render(<Header/>) but do not pass any props to the component. You need to provide Header with the required props.
render(<Header value={whateverValueShouldBe} />)
Ive been using this project with out a problem and now all of a sudden I keep getting this error and it won't show my notes when I click on the my notes section. What do I have to do for it to go away. The backend is up and running and I can see the static data but it wont show on the app
import { makeStyles } from '#mui/styles'
import React from 'react'
import { Drawer } from '#mui/material'
import { Typography } from '#mui/material'
import List from '#mui/material/List'
import ListItem from '#mui/material/ListItem'
import ListItemIcon from '#mui/material/ListItemIcon'
import ListItemText from '#mui/material/ListItemText'
import { AddCircleOutlineOutlined, SubjectOutlined } from '#mui/icons-material'
import { useHistory, useLocation } from 'react-router-dom'
import AppBar from '#mui/material/AppBar'
import Toolbar from '#mui/material/Toolbar'
import { format } from 'date-fns'
import { red } from '#mui/material/colors'
const drawerWidth = 240 // 500 - subtract this number from
const useStyles = makeStyles((theme) => {
return{
page: {
background: '#E5E4E2',
width: '100%',
padding: theme.spacing(3)
},
drawer: {
width: drawerWidth
},
drawerPaper: {
width: drawerWidth
},
root: {
display: 'flex' //places the drawer side by side with the page content
},
active: {
background: '#E5E4E2'
},
// title:{
// padding: theme.spacing(13),
// alignItems: 'center'
// },
}})
export default function Layout({ children }) {
const classes = useStyles()
const history = useHistory()
const location = useLocation()
const menuItems = [
{
text: 'My Projects',
icon: <SubjectOutlined color="secondary" />,
path: '/'
},
{
text: 'Create Project',
icon: <AddCircleOutlineOutlined color="secondary" />,
path: '/create'
}
]
return (
<div className={classes.root}>
{/* side drawer */}
<Drawer
className={classes.drawer}
variant='permanent' //Lets MUI know we want it on the page permanently
anchor="left" // position of drawer
classes={{ paper: classes.drawerPaper}}
>
<div>
<Typography variant="h5" sx={{textAlign: 'center'}}>
Projects
</Typography>
</div>
{/* List / Links */}
<List>
{menuItems.map(item => (
<div className={location.pathname == item.path ? classes.active : null}>
<ListItem key={item.text} button onClick={() => history.push(item.path)}>
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={item.text} />
</ListItem>
</div>
))}
</List>
</Drawer>
<div className={classes.page}>
<div className={classes.toolbar}></div>
{children}
</div>
</div>
)
}
enter image description here
Updated
I'm sorry, of course, you should just move key to the parent div. I didn't notice it. Chris who answered in the comments is right and my answer was not needed. I rewrote the answer.
To have an unique key use index in map or like you did item.text if text is unique for each element in map.
menuItems.map((item,index) =>
The idea is that map has to contain unique key for each element.
In result we have:
<div key={item.text} className={location.pathname == item.path ? classes.active : null}>
or
<div key={index} className={location.pathname == item.path ? classes.active : null}>
And you need to remove key from the List.
Hope this helps! Regards,
So this my movies.js component and i wanted to pass the data movies to my other component where i want to display the data on cards but i dont know how to use the promise function :
const movies = [
{
id: '1',
title: 'Oceans 8',
category: 'Comedy',
likes: 4,
dislikes: 1
}, {
id: '2',
title: 'Midnight Sun',
category: 'Comedy',
likes: 2,
dislikes: 0
},
]
export const movies$ = new Promise((resolve, reject) => setTimeout(resolve, 100, movies))
This is my other component where i want to display on cards i tried to import the const movie$ but it didn't work:
import React, { Component } from "react";
import { Card, Button } from "react-bootstrap";
import { movies$ } from "../Data/movies";
export default class Movies extends Component {
render() {
return (
<div>
<h1>Movies</h1>
<Card style={{ width: "18rem" }}>
<Card.Img variant="top" src="holder.js/100px180" />
<Card.Body>
<Card.Title>
<movies$
/>
</Card.Title>
<Card.Text>
Some quick example text to build on the card title and make up the
bulk of the card's content.
</Card.Text>
<Button variant="primary">Supprimer</Button>
</Card.Body>
</Card>
</div>
);
}
}
You need to create a property in your component state to store the movies, then, fill it when Promise is resolved:
import { movies$ } from "../Data/movies";
export default class Movies extends Component {
constructor() {
this.state = {
movies: []
}
}
componentDidMount() {
movies$
.then(data => this.setState({movies: data}))
}
//...
}
Iterate over movies to render the data:
ie:
<Card.Title>
{this.state.movies.map(movie => (<div>{movie.title}</div>))}
</Card.Title>
You don't need a promise. To display data from array use the map() function
movies.js
const movies = [
{
id: "1",
title: "Oceans 8",
category: "Comedy",
likes: 4,
dislikes: 1
},
{
id: "2",
title: "Midnight Sun",
category: "Comedy",
likes: 2,
dislikes: 0
}
];
export default movies;
App.js
import React, { Component } from "react";
import { Card, Button } from "react-bootstrap";
import movies from "./movies";
export default class Movies extends Component {
render() {
return (
<div>
<h1>Movies</h1>
{/* map function below */}
{movies.map(movie => (
<Card key={movie.id} style={{ width: "18rem" }}>
<Card.Img variant="top" src="holder.js/100px180" />
<Card.Body>
<Card.Title>{movie.title}</Card.Title>
<Card.Text>
Some quick example text to build on the card title and make up
the bulk of the card's content.
</Card.Text>
<Button variant="primary">Supprimer</Button>
</Card.Body>
</Card>
))}
</div>
);
}
}
Demo: stackblitz
I am a newbie to React. I am trying to add links to the React semantic UI dropdown menu. Following is the component that I fetched from React semantic UI
import React from "react";
import { Dropdown } from "semantic-ui-react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
const trigger = (
<span style={{ marginTop: "2px" }}>
<i aria-hidden="true" class="user big icon" size="big" />
</span>
);
const options = [
{ key: "user", text: "Account", icon: "user", to: "/accounts" },
{ key: "settings", text: "Settings", icon: "settings", to: "/settings" },
{ key: "sign-out", text: "Sign Out", icon: "sign out", to: "/sign-out" }
];
const DropdownMenu = () => (
<Dropdown
trigger={trigger}
options={options}
pointing="top right"
icon={null}
/>
);
export default DropdownMenu;
I tried adding "to" keyword to the options array and have added the Route paths to the Router which lies in a different component but it's not working. Any help would be appreciated, Thank you.
Or you can use Dropdown.Menu and Dropdown.Item. Each Dropdown.Item can have param as={Link} to='/somewhere'/.
import React from "react";
import { Dropdown } from "semantic-ui-react";
import { Link } from "react-router-dom";
const trigger = (
<span style={{ marginTop: "2px" }}>
<i aria-hidden="true" class="user big icon" size="big" />
</span>
);
const DropdownMenu = () => (
<Dropdown trigger={trigger} pointing='top left' icon={null}>
<Dropdown.Menu>
<Dropdown.Item text='Account' icon='user' as={Link} to='/accounts'/>
<Dropdown.Item text='Settings' icon='settings' as={Link} to='/settings'/>
<Dropdown.Item text='Sign Out' icon='sign out' as={Link} to='/sign-out'/>
</Dropdown.Menu>
</Dropdown>
);
export default DropdownMenu;
I hope it will help you.
You can try something like this. change the key to to value in you options array. then use onChange method to handle the selection. You may want to pass the history prop from parent component down to the component you want to invoke the action if its not available.
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Dropdown, Form } from "semantic-ui-react";
const trigger = (
<span style={{ marginTop: "2px" }}>
<i aria-hidden="true" class="user big icon" size="big" />
</span>
);
const options = [
{ key: "user", text: "Account", icon: "user", value: "/accounts" },
{ key: "settings", text: "Settings", icon: "settings", value: "/settings" },
{ key: "sign-out", text: "Sign Out", icon: "sign out", value: "/sign-out" }
];
class App extends Component {
move = (e, { value }) => {
this.props.history.push('value')
}
render() {
return (
<div>
<Dropdown
options={options}
trigger={trigger}
icon={null}
onChange={this.move}
/>
</div>
);
}
}
My solution based on the answer from Jan Fara:
import React from 'react';
import { Dropdown } from 'semantic-ui-react';
import Avatar from 'react-avatar';
import { Link } from 'react-router-dom';
function DropdownuserMenu() {
const trigger = (
<span>
<Avatar name='Happy User' size="40"/>
</span>
);
return (
<Dropdown trigger={trigger} pointing='top left' icon={null}>
<Dropdown.Menu>
<Dropdown.Item text='Account' icon='user' as={Link} to='/accounts'/>
<Dropdown.Item text='Settings' icon='settings' as={Link} to='/settings'/>
<Dropdown.Item text='Sign Out' icon='sign out' as={Link} to='/sign-out'/>
</Dropdown.Menu>
</Dropdown>
);
}
export default DropdownuserMenu;