Gatsby.js issue with frontmatter in graphql - reactjs

Looking for some help with this issue I have when trying to render the frontmatter for my project list. Essentially, it's not able to "detect frontmatter" nor "AllMdx", but when I query in graphql locally there is no issue. I get the information I need from the query, except I cannot grab the image.
This is the start of my .mdx file, where I define the image, I also tried other variations:
image: "../images/icon.png"
image: ../images/icon.png
image: icon.png
None seem to work.
MT.mdx start of file
---
slug: "/projects/MT"
date: "2019-09-04"
title: "MT"
image: "icon.png"
---
Projects.jsx
import React from "react"
import {ContentContainer} from "../components/Common/Container";
import { StaticQuery, graphql } from "gatsby";
import Card from '#mui/material/Card';
import CardContent from '#mui/material/CardContent';
import CardMedia from '#mui/material/CardMedia';
import Typography from '#mui/material/Typography';
// import { Button, CardActionArea, CardActions } from '#mui/material';
export default function Projects() {
return (
<StaticQuery
query={graphql`
query ProjectsQuery {
allMdx(filter: { fileAbsolutePath: { regex: "/projects/" } }) {
nodes {
frontmatter {
image
slug
title
date
}
excerpt
}
}
}
`}
render={data => (
<ContentContainer>
{data.allMdx.nodes.map(({ frontmatter, excerpt }) => (
<Card sx={{ maxWidth: 360 }}>
<CardMedia
component="img"
height="180"
image={frontmatter.image}
alt="placeholder"
/>
<CardContent>
<Typography gutterBottom variant="h4" component="div">
{frontmatter.title}
<br/>
{frontmatter.slug}
<br/>
{frontmatter.date}
<br/>
{excerpt}
</Typography>
</CardContent>
</Card>
)
)}
</ContentContainer>
)}
></StaticQuery>
)}
Result in localhost

My bet is that the path of the image is not correct.
You are passing a full_DCD.png but that path I'm not sure if it's correct (it will rely on your project structure). This assumes that there's a full_DCD.png image at localhost:8000/full_DCD.png but it looks like it should be somewhere inside /projects/MT/full_DCD.png but as I said, without knowing your project structure it's difficult to guess.
Try spotting the valid image path where the image is located (i.e: checking the /public folder or playing around with the URL to get the image).
Maybe you just simply need to concatenate the project slug, like:
{
data.allMdx.nodes.map(({ frontmatter, excerpt }) => (
<Card sx={{ maxWidth: 360 }}>
<CardMedia
component="img"
height="180"
image={`${frontmatter.slug}${frontmatter.image}`}
alt="placeholder"
/>
<CardContent>
<Typography gutterBottom variant="h4" component="div">
{frontmatter.title}
<br />
{frontmatter.slug}
<br />
{frontmatter.date}
<br />
{excerpt}
</Typography>
</CardContent>
</Card>
));
}
Depending on your structure, you just may need to add image={`/${frontmatter.image}`}.

Related

Mapping Horizontal divider using Material UI react

I'm working on menu component storybook where i have mapped icon & text , problem is i have horizontal divider in between , how do i Map it with icons & text.From below code i'm getting divider at the bottom of the Menu. I'm trying to achieve as it is in the image. In the storybook i have to map few menu variants as in mui, for that i cannot hardcode divider, if there is any way i can manage it with Map or any other method. Thanks.
export const Menu= ({ icons }) => {
return (
<Paper sx={{ width: 320, maxWidth: '100%' }} >
<MenuList>
{icons.map((item) => (
<MenuItem>
<ListItemIcon fontSize="small">{item.icon}</ListItemIcon>
<ListItemText>{item.label}</ListItemText>
<Typography variant="body2" color="text.secondary">{item.typography}</Typography>
</MenuItem>
))}
<Divider /> /* how do i map this */
</MenuList>
</Paper >
);
}
Stories.js
icons: [
{ icon: <ContentCut fontSize="small" />, typography: "⌘X", label: "Cut" },
{ icon: <ContentCopy fontSize="small" />, typography: "⌘C", label: "Copy" },
]
Answer:
If you want the divider to be just one then don't map over it . thats the purpose of the .map() method.
and To Acheive the required results i just removed <Menu></Menu> Component and Just Kept the <Papper></Papper> Component
Notes :
In terms of how to Map the Divider with the below example ,you can just wrap it in a empty react fragment<></> and map over the <MenuItem></MenuItem> .
Only issue is that youll get an error in your key props which will say its not unique it can be fixed by assigning index key like the example below and wrap the <MenuItem></MenuItem> Component in It. However thats not best practice ,
<React.Fragment></React.Fragment> Is better practice according to Keyed Fragment React 18 Docs to add a key prop However that's giving a New Error in MUI.
Thats not an issue since were mapping over the MenuItem Component , However if we use for example in #Nathan Comments <React.Fragment key={index}></React.Fragment> or my previous answer to use <></> we would be mapping over the React.Fragment Itself or the empty fragment and would get a new error MUI: The Menu component doesn't accept a Fragment as a child. Uncomment the Examples in the Sandbox Check the sandbox console.
Check Code Code SandBox
Solution
export const MenuIcon = ({ menuicons }) => {
return (
<Paper sx={{ width: 320, maxWidth: "100%" }}>
{menuicons.map((item, index) => (
<MenuItem key={item + index}>
<ListItemIcon fontSize="small">{item.icon}</ListItemIcon>
<ListItemText>{item.label}</ListItemText>
<Typography variant="body2" color="text.secondary">
{item.typography}
</Typography>
</MenuItem>
))}
<Divider />
<ListItemIcon fontSize="small">
ClipBoard <ContentCopyIcon fontSize="small" />
</ListItemIcon>
</Paper>
);
}
References
Empty Fragment Syntax React Docs
Stack OverFlow Question MUI Icons - as props
.map() method Syntax - MDN Docs

dynamically load an image as a prop in react

I am trying to load images dynamically in react and pass them as props. I have a data.json file that is basically an array of article objects.
title:"article1"
headline:"headline1"
image:"/src/images/articleimage1"
articleContent:"ipsum lorem"
in the first component I map through the array and return a "card" with props
const featuredArticles = imageCardArray.map(elem => <ImageCard
title={elem.title}
headline = {elem.headline}
image= {elem.image}
/>)
return (
<div className ="center">
{featuredArticles}
</div>
then I use those props in the card component itself. It all works perfectly except for the images
function ImageCard(props) {
return (
<div className="imageCard">
<Card>
<CardActionArea>
<CardMedia
component="img"
image={props.image}
title={props.title}
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{props.title}
</Typography>
<Typography variant="body2" color="textSecondary" component="p">
{props.headline}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button size="small" varient="outlined">
Artigo completo
</Button>
</CardActions>
</Card>
</div>
);
}
export default ImageCard;
any ideas how to load these images? the card component is from material UI if it helps. (it says in the docs that the image property just accepts a string)
I can get the image to load in the card by importing it at the top and then image = {image}, but of course then it is not dynamic and all the cards have the same image. please help!! thanks
You need to import the image from directory at top
import ArticleImg1 from 'image path';
and then construct your array of objects like this
{
title:"article1"
headline:"headline1"
image:ArticleImg1
articleContent:"ipsum lorem"
}
If I understand your question correctly it will work.
the answer is here https://medium.com/react-courses/5-top-methods-to-import-and-load-images-dynamically-on-cra-react-local-production-build-855d3ba3e704
you need to have in your json just the file name for the image, not the full path.
title:"article1"
headline:"headline1"
image:"articleimage1"
articleContent:"ipsum lorem
then in the card component:
you can either assign the resource to a component ...
const Image = require( '/src/images/'+ props.image);
and in the JSX
<img className="card-img-top" src={ Image } alt="image not found" />
or do the require in the jsx
<img className="card-img-top" src={ require( '/src/images/'+ props.image) } alt="image not found" />
assumption made that all images will be in the same folder...

React Bootstrap Cards and CardDeck Components - responsive layout

I'm using React Bootstrap and am pulling data from an API using Axios. I'm trying to display the data from that API in bootstrap Card / CarDeck components. I have most of it working using the code below but I'm having problems with the layout.
I'd like to be able to have the card deck be responsive and show a flexible number of cards per row depending on the screen size. At the moment if there are lots of items in the response each card is ridiculously thin and can't be viewed. What is the correct way to do this?
import React, { useState, useEffect } from "react";
import CardDeck from "react-bootstrap/CardDeck";
import Card from "react-bootstrap/Card";
import axios from "axios";
import Container from "react-bootstrap/Container";
const CardColumns = () => {
const [cardInfo, setData] = useState([]);
console.log(cardInfo);
useEffect(() => {
axios
.get(
"https://webhooks.mongodb-realm.com/api/client/v2.0/app/cards-fvyrn/service/Cards/incoming_webhook/getAllCards"
)
.then((response) => {
setData(response.data);
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
}, []);
const renderCard = (card, index) => {
return (
<Card style={{ width: "18rem" }} key={index} className="box">
<Card.Img variant="top" src="holder.js/100px180" src={card.image} />
<Card.Body>
<Card.Title>
{index} - {card.manufacturer}
</Card.Title>
<Card.Text>{card.player}</Card.Text>
</Card.Body>
</Card>
);
};
return <CardDeck>{cardInfo.map(renderCard)}</CardDeck>;
};
export default CardColumns;
In the end this is the solution I went with the below. Key I think were:
Wrapping the cardDeck in <Container> and then wrapping the cards with <Col className="container-fluid mt-4">:
`
const renderCard = (card, index) => {
return (`
<Col className="container-fluid mt-4">
{/* <Card key={index} className="box"> */}
<Card style={{ width: "18rem" }} key={index} className="box">
<Card.Header>
{card.brand} - {card.series}
</Card.Header>
<Card.Img variant="top" src={card.front_image} fluid />
<Card.Body>
<Card.Title>
{card.player} (#
{card.card_number.$numberDouble}) {card.variation}
</Card.Title>
{/* <Card.Text className="d-flex">{card.player}</Card.Text> */}
{/* <Card.Text>{card.player}</Card.Text> */}
</Card.Body>
<ListGroup className="list-group-flush">
<ListGroupItem>
Print Run - {card.print_run.$numberDouble}
</ListGroupItem>
<ListGroupItem>Career Stage - {card.career_stage} </ListGroupItem>
<ListGroupItem>For Trade - {card.forTrade}</ListGroupItem>
</ListGroup>
<Card.Footer className="text-muted">{card.team}</Card.Footer>
</Card>
</Col>
//{/* </Col> */}
);
};
return (
<Container>
<Button variant="primary">Filter By Brand</Button>{" "}
<Button variant="primary">Filter By Player</Button>{" "}
<Button variant="primary">Filter By Team</Button>
<CardDeck>{cardInfo.map(renderCard)}</CardDeck>
</Container>
);`
Here are my suggestions for this issue:
Each card should have a min-width to ensure that they do not shrink below a certain amount. So instead of width: "18rem" try min-width: "18rem". If your CSS is properly set up it should cause other cards to overflow to the next row.
You can make use of media-queries or grid layout as mentioned to determine how many cards you want to show for various screen types based on their varying widths say see this link media-query-breakpoints react-bootstrap-grid
Also you can try using CSS flexbox layout, I have an article on this CSS Flex Box
Try to use a col-'n' to each card instead of a fix width.
...
import { Card, Col } from 'react-bootstrap';
...
<Col md="n"> // n must be your desired width like
<Card key={index} className="box">
...
</Col>
You can see more at: Bootstrap Card Sizing

Bootstrap card in react

I'm suffering from implementing bootstrap in react. I set up bootstrap4 at the beginning but I got a bad layout then used react-bootstrap and I get the same issue. Th card body,title and button appear on the image itselfenter image description here
import React from 'react';
import { Card, Button } from 'react-bootstrap';
const WorkCard = () => {
return (
<div>
<Card style={{ width: '18rem' }} >
<Card.Img variant="top" src="holder.js/100px180" />
<Card.Body>
<Card.Title>Card 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">Go somewhere</Button>
</Card.Body>
</Card >
</div>
)
}
export default WorkCard;
Using sass was overriding the bootstrap so I fixed that.

How to make material ui icon clickable and redirect to url

I want to make GitHub icon <GitHubIcon /> clickable and make it redirect to external url, looking in the api it doesn't seem to have link prop ... https://material-ui.com/api/svg-icon/
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" className={classes.title}>
Made with <FavoriteIcon /> by
<GitHubIcon /> //Not working
</Typography>
</Toolbar>
</AppBar>
</div>
How is it done usually ??
Add this:
<GitHubIcon onClick={handlePageChange} />
and on the function definition:
const handlePageChange() {
window.location.href="pagelink"
}
or directly using an arrow function:
<GitHubIcon onClick={event => window.location.href='pagelink'} />
If looking to add effects on hover with material-ui styles:
How to add stlyes on Material-UI:
Import makeStyles:
import { makeStyles } from '#material-ui/core/styles';
Create the class:
const useStyles = makeStyles(theme => ({
clickableIcon: {
color: 'green',
'&:hover': {
color: 'yellow',
},
},
}));
Add the API call on your function declaration:
const classes = useStyles();
And add this class to your element:
<GitHubIcon
onClick={event => window.location.href='pagelink'}
className={classes.clickableIcon}
/>
You could also use directly a css file.
I'm not really happy with css styling over material-ui api, specially because this horrible CSS-in-JS syntaxi, but it works well, here you'll find some documentation: Material-ui Styles

Resources