I'm trying to add a className of split to my header, based on the page template as defined in the frontmatter of a page. I've got a page template called split and want the header to have that class on pages that are utilizing the template. Here are my templates:
layout.js
import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import { StaticQuery, graphql } from 'gatsby'
import Header from './header'
import Footer from './footer'
import '../css/main.scss'
import favicon from './favicon.png'
const Layout = ({ children, frontmatter }) => (
<StaticQuery
query={graphql`
query PageInfoQuery {
site {
siteMetadata {
title
footer
}
}
markdownRemark {
frontmatter {
template
}
}
}
`}
render={data => (
<>
<Helmet
title={data.site.siteMetadata.title}
meta={[
{ name: 'description', content: 'Sample' },
{ name: 'keywords', content: 'sample, something' },
]}
link={[
{
rel: 'shortcut icon',
type: 'image/png',
href: `${favicon}`,
},
]}
>
<html lang="en" />
</Helmet>
<Header siteTitle={data.site.siteMetadata.title} frontmatter={data.markdownRemark.frontmatter}/>
{children}
<Footer footer={data.site.siteMetadata.footer} />
</>
)}
/>
)
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout
header.js
import PropTypes from 'prop-types'
import React from 'react'
import Navbar from './navbar'
const Header = ({ siteTitle, frontmatter }) => (
<header className={ frontmatter.template === 'split' ? 'header split' : 'header' }>
<section className="navigation">
<Navbar siteTitle={siteTitle} />
</section>
</header>
)
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: '',
}
export default Header
I know that I've got the data.markdownRemark.frontmatter working correctly, as in my template split.js I'm able to display the template name dynamically in the hero element, using {post.frontmatter.template}. In the interest of being thorough I'll include that as well.
split.js
import React from 'react'
import Helmet from 'react-helmet'
// eslint-disable-next-line
import { Link, graphql } from 'gatsby'
import Layout from '../components/layout'
// import blocks
import Feature from '../components/blocks/feature'
import Hero from '../components/blocks/hero'
class BlocksTemplate extends React.Component {
render() {
const post = this.props.data.markdownRemark
const siteTitle = this.props.data.site.siteMetadata.title
const siteDescription = post.excerpt
const heroImage = post.frontmatter.hero_image.childImageSharp.fixed.src
return (
<Layout location={this.props.location} title={siteTitle}>
<Helmet
htmlAttribute={{ lang: 'en' }}
meta={[{ name: 'description', content: siteDescription }]}
title={`${post.frontmatter.title} • ${siteTitle}`}
/>
<section className="hero" style={{ backgroundImage: `url(${heroImage})` }}>
<h4 className="name">
<Link to="/">
{this.props.siteTitle}
{post.frontmatter.template}
</Link>
</h4>
</section>
{post.frontmatter.blocks.map(block => {
switch (block.component) {
case 'feature':
return <Feature block={block} />
case 'hero':
return <Hero block={block} />
default:
return ''
}
})}
</Layout>
)
}
}
export default BlocksTemplate
export const pageQuery = graphql`
query SplitPageBySlug($slug: String!) {
site {
siteMetadata {
title
}
}
markdownRemark(fields: { slug: { eq: $slug } }) {
id
excerpt
html
frontmatter {
template
title
hero_image {
childImageSharp {
fixed(width: 1500) {
src
}
}
}
blocks {
component
image {
childImageSharp {
fixed(width: 1500) {
src
}
}
}
}
}
}
}
`
Here is the header markup that is being rendered; in essence the split class is never being added.
<header class="header">
<section class="navigation">
<nav class="nav" role="navigation">
<div class="branding">
<h6 class="name">Jesse Winton</h6>
</div>
Home
<a aria-current="page" class="" href="/about-the-demo">What is this?</a>
About Gatsby
Blog
</nav>
</section>
</header>
I'm fairly new to Gatsby, so any help would be very much appreciated! Thank you!
In layout.js, you are passing frontmatter={data.markdownRemark.frontmatter} to the header from the query above it in layout.js. This is a staticQuery which will always return the same data.
I see you are querying for the correct data in the pageQuery in split.js, but this data is not passed along to the layout in split.js:
<Layout location={this.props.location} title={siteTitle}>
One way to fix this is to pass the template name to the layout props, just like you are already doing with location and title attributes.
Another way would be using the gatsby-plugin-layout plugin which return the old behavior from V1 Gatsby for the Layout component, adding a layout wrapper to every component. Then you could the pageContext to the split component directly.
Related
Can we use multiple times inside the . I have with some options ,only these 2 routes should be placed in row and column view
App.js
import useRoutes from "./useRoutes";
export default App=()=>{
return(
<BrowserRouter>
{ <div className='row'>
<div className= 'col'>SideBar</div>
<div className= 'col'>{Routes}</div>
</div>} /*--displays only when user logged in,user menu --*/
<Routes />
</BrowserRouter>
)
}
Routes.js
import { useRoutes } from "react-router-dom";
function Routes() {
let element = useRoutes([
{
path: "/",
element: <Dashboard />,
},
{
path: "tasks",
element: <DashboardTasks /> },
},
{ path: "team",
element: <AboutPage /> },
]);
return element;
}
SideBar.js
export default SideBar=()=>{
return(
<ul>
<li>AboutPage</li>
<li>DashboardTasks</li>
</ul>
)
}
How to implement it ,how to show the routes in half side only in sidebar
I am very new to react and trying to work on my first website.
I have tried to seek my problem online, I've encountered similar questions to mine but could not figure out my exact problem.
My layout of components are in a scroll down style (portfolio), when I try route for example to my contact component it wont render unless I refresh my page. also instead of scrolling down to component it will pop up at the top .(Hope I am clear)
My App Function
import { useState } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import './App.css';
import Contact from './Components/Contact/Contact';
import Form from './Components/FormArea/Form/form';
import Introdoction from './Components/Introdoction/Introdoction';
import NavBar from './Components/NavBar/NavBar';
import NavigationBar from './Components/NavigationBarHeader/NavigationBar/NavigationBar';
import Portfolio from './Components/Portfolio/Portfolio';
import Routing from './Components/Routing/Routing';
import Skills from './Components/Skills/Skills';
function App() {
return (
<BrowserRouter>
<Switch>
<div className="App">
<NavigationBar/>
<section className="section">
<Route path="/contact" component={Contact} exact/>
<Introdoction/>
<Portfolio/>
<Skills/>
{/* <Contact/> */}
<Form/>
</section>
</div>
</Switch>
</BrowserRouter>
);
}
export default App;
I am trying to configure "Contact" component for this example.
My Menu/Navbar Component
import { Component, MouseEventHandler } from "react";
import {MenuItems} from "../MenuItems/MenuItems";
import "./NavigationBar.css";
import icon from '../../../Assets/icon.png'
import {Button} from "../Button/Button";
import { NavLink, Redirect, Route, Switch, useHistory } from "react-router-dom";
import Contact from "../../Contact/Contact";
class NavigationBar extends Component {
state = {clicked : false}
handleClick = () => {
this.setState ( { clicked: !this.state.clicked } );
};
public render(): JSX.Element {
return (
<nav className="NavbarItems ">
<img className="navbar-logo"src={icon } />
<div className="menu-icon" onClick={this.handleClick}>
<i className={this.state.clicked ? 'fas fa-times' : 'fas times'}></i>
</div>
<ul className={this.state.clicked ? 'nav-menu active' : 'nav-menu'}>
{MenuItems.map((item,index) => {
return (
<li key={index}>
<NavLink to={item.url} className={item.cName} >{item.title} </NavLink>
</li>
)
})}
</ul>
<Button>Sign up</Button>
</nav>
);
}
}
export default NavigationBar;
ItemMenu Component
import { NavLink } from "react-router-dom";
import "./MenuItems.css";
export const MenuItems = [
{
title: 'Home',
url: '/Home',
cName: 'nav-links'
},
{
title: 'Introduction',
url: '/introduction',
cName: 'nav-links'
},
{
title: 'Skills',
url: '/skills',
cName: 'nav-links'
},
{
title: 'Projects',
url: '/projects',
cName: 'nav-links'
},
{
title: 'Contact',
url: '/contact',
cName: 'nav-links'
},
];
I could add more information if needed. I hope my question is clear on what my problem is.
Thanks.
If I understood your question correctly, you want one single page, with everything below each other, and when you press a link, it scrolls you down to that place. In that case I would use:
<Contact id="contact" />
react-router creates new sub pages, which you don't want (if I understood you correctly)
I want to pass my data from one page to another but in an specific way.
I have a json brought via AJAX with this method (resp, error and result):
myArrayItems
import React, { Component } from 'react';
import logo from '../images/logo.png';
import '../App.css';
import { Container, Row, Col } from 'react-bootstrap';
import { Route, IndexRoute } from 'react-router';
import { BrowserRouter as Router, Switch, Link } from 'react-router-dom';
class myArrayItems extends Component {
componentDidMount() {
fetch("https://myrestserver/myArrayItems")
.then(response => response.json())
.then(
(result) => {
console.log(result)
this.setState({
isLoaded: true,
items: result
});
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
And in my View i rendered like that with key.
<ul>
{items.map(item => (
<li key={item.title} className="list-group-item border-0">
<div className="wrapper">
<div className="myclass">
<Link to={{ pathname: "/myitem/" }} className="App-link">
<p className="text-left">{item.title}</p>
</Link>
</div>
</div>
</li>
))}
</ul>
}
export default myArrayItems;
So I want to pass the item.id, item.title, item.x to the page myitem, but i imported the myArrayItems and still get undefined.
What I want to achieve is in my first class to display a list of items (What i did) but when i click on any of these items, to access them in the page myitem.
myitem.js
import React, { Component } from 'react';
import logo from '../images/logo.png';
import '../App.css';
import { Container, Row, Col } from 'react-bootstrap';
import { Route, IndexRoute } from 'react-router';
import myArrayItems from './myArrayItems';
const Background = {
background: '#004272'
}
class myitem extends Component {
render(){
return (
<div className="App">
<header style={Background} className="text-center py-2 m-0">
<img src={logo} className="img-fluid w-75"/>
</header>
<Container>
<ul>
<p>{item.title}</p>
</ul>
</Container>
</div>
);
}
}
export default myitem;
I hope you can help me with that, thanks!
UPDATE:
I'm creating a WebApp using ReactJS Library with "npx create-react-app".
I've got the following code which is looping through an JSON file from an API and loops through some posts.
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
//https://alligator.io/react/axios-react/
import axios from 'axios';
export default class PostList extends React.Component {
state = {
posts: []
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
const posts = res.data;
this.setState({ posts });
})
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
Pulls in post slugs from Domain
</p>
<ul>
{ this.state.posts.map(post => <li>{post.name} - {post.username} </li>)}
</ul>
</div>
)
}
}
This works fine, and gets the information which was needed.
Now, in my test JSON file, the format is as follows:
https://jsonplaceholder.typicode.com/users
But in my actual JSON file from WordPress Rest API, we have another item, named core_layout:
JSON image
My issue is, trying to use the same code such as {post.name}does not get the information needed such as core_layout->image->name.
Is there an easy way around this?
Thanks all!
EDIT:
Tried the answers below, but still no luck, get the error TypeError: Cannot read property 'map' of undefined:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
//https://alligator.io/react/axios-react/
import axios from 'axios';
export default class PostList extends React.Component {
state = {
posts: [],
coreLayout: {}
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
// const posts = res.data;
//this.setState({ posts });
const { posts, core_layout: coreLayout } = res.data;
this.setState({ posts, coreLayout });
})
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
Pulls in post slugs from domain
</p>
<ul>
{ this.state.posts.map(post => <li>{post.name} - {post.core_layout.image.name}</li>)}
</ul>
</div>
)
}
}
EDIT 2:
Tried the below: This gets the title, but again, not the actual corelayout I need.
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
movies: []
}
}
componentDidMount() {
let dataURL = "http://zinsseruk.com/wp-json/wp/v2/posts?per_page=1";
fetch(dataURL)
.then(res => res.json())
.then(res => {
this.setState({
movies: res
})
})
}
render() {
let movies = this.state.movies.map((movie, index) => {
return <div key={index}>
<p><strong>Title:</strong> {movie.title.rendered}</p>
<p><strong>Title:</strong> {movie.core_layout.acf_fc_layout}</p>
</div>
});
return (
<div>
<h2>Star Wars Movies</h2>
{movies}
</div>
)
}
}
export default App;
Replace const posts = res.data; with const posts = res.data.core_layout;. Then you'll get an array similar to what you have in your test file.
I think you need to understand the JSON structure you receive from the API. Where is located core_layout property? Inside each post property as a children?
So in the posts loop you can use post.core_layout.image.name for image name, for example (and so on with other properties).
If core_property is at the root of the data you receive, you can load it inside your state like so:
state = {
posts: [],
coreLayout: {}
}
componentDidMount() {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
// This is equivalent of doing
// const posts = res.data.posts
// const coreLayout = res.data.core_layout
const { posts, core_layout: coreLayout } = res.data;
this.setState({ posts, coreLayout });
})
}
Then use it in your code by using local component state:
render() {
...
// For example image name:
console.log('image name', this.state.coreLayout.image.name)
...
}
I have a navigation component that uses Link, IndexLink to render the navigation bar:
class Navigation extends Component {
renderLinks = (linksData) => {
return linksData.map((link) => {
if(link.to === '/') {
return (
<li key={link.text}>
<IndexLink to={link.to}>
<i>{link.icon}</i>
<span>{link.text}</span>
</IndexLink>
</li>
);
} else {
return (
<li key={link.text}>
<Link to={link.to}>
<i>{link.icon}</i>
<span>{link.text}</span>
</Link>
</li>
);
}
});
};
render() {
const {links} = this.props;
return(
<div>
<ul>
{this.renderLinks(links)}
</ul>
</div>
)
}
}
Navigation.propTypes = {
links: React.PropTypes.array.isRequired
}
export default Navigation;
Now I want to test that my links render correcttly:
import React from 'react';
import {Link, IndexLink} from 'react-router';
import {mount} from 'enzyme';
import Navigation from '../components/core/Navigation.component';
describe('<Navigation/>', () => {
it('should render Navigation links', () => {
const links = [
{
to: '/',
icon: 'hourglass_empty',
text: 'Timer'
}
];
const navigation = mount(<Navigation links={links}/>);
console.log(navigation.find('a').prop('href'));
});
});
But this just logs out undefined. How am I able to test that the correct href value is getting passed to my anchors?
try this:
wrapper.find(Foo).at(0).props().href
This question is related to React, but for Vue, the following code works:
wrapper.find('.link').attributes('href')
The link has a prop of to in React to query the link find it by the text you click - and the right attribute is to not href
eg:
const url1 = screen.getByText("I am a link click me")
expect(url1).toHaveAttribute("to", "https://test-url1#")