bulma menu toggle in gatsbyjs not working - javascript

GatsbyJS beginner here, trying to get the Bulma responsive menu toggle to apply the "is-active" class to the menu (gatsby v2 with gatsby starter netlify cms). all code here: https://github.com/pddew/gatsby-starter-netlify-cms
Currently the toggle button and script tags appear but the button doesn't respond.
There is a working version of this in the gatsby starter business- when I inspect and compare my site with this, I can't spot the error, only that there is no event listener on toggle button, when it seems there should be.
When I inspect the site, the toggle.js script is being called and put in before the closing body tag, and the viewed.
I have tried building and deploying with no luck, clearing caches and swapping the scripts for bulma's suggested code.
Here is the relevant code.
Any help with this greatly appreciated; I'm a bit stuck!
in Layout.js:
import React from 'react' import Helmet from 'react-helmet'
import Navbar from '../components/Navbar' import Footer from '../components/Footer' import './all.sass'
const TemplateWrapper = ({ children }) => ( <div>
<Helmet title="Immediate Start Jobs" />
<Navbar />
<div>{children}</div>
<Footer /> </div> )
export default TemplateWrapper
in Navbar:
<button className="button navbar-burger" data-target="navMenu">
<span />
<span />
<span />
</button>
</div>
<div className="navbar-menu" id="navMenu">
<div className="navbar-start">
<Link className="navbar-item" to="/about">
About
</Link>
<Link className="navbar-item" to="/products">
Products
</Link>
<Link className="navbar-item" to="/blog">
Blog
</Link>
</div>
in html.js
import React from "react"
import PropTypes from "prop-types"
export default class HTML extends React.Component {
render() {
return (
<html {...this.props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
{this.props.headComponents}
</head>
<body {...this.props.bodyAttributes}>
{this.props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: this.props.body }}
/>
{this.props.postBodyComponents}
<script src={__PATH_PREFIX__ + '/js/toggle.js'} />
</body>
</html>
)
}
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
And my toggle.js, stored in static/js/
document.addEventListener('DOMContentLoaded', function () {
// Get all "navbar-burger" elements
var $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0)
// Check if there are any navbar burgers
if ($navbarBurgers.length > 0) {
// Add a click event on each of them
$navbarBurgers.forEach(function ($el) {
$el.addEventListener('click', function () {
// Get the target from the "data-target" attribute
var target = $el.dataset.target
var $target = document.getElementById(target)
// Toggle the className on both the "navbar-burger" and the "navbar-menu"
$el.classList.toggle('is-active')
$target.classList.toggle('is-active')
})
})
}
})

I'm new to Gatsby myself, but I had to find a way to get my navbar to function. My solution is pretty quick and dirty, but it works. If your building a website with a lot of components that change state, I would suggest using redux and a central store instead. My solution was just having a navbar that handled its own state.
import React, { Component } from 'react'
import Link from 'gatsby-link'
class Navbar extends Component {
state = {
//This sets the state of Bulma elements
navbarIsActive: "navbar-item has-dropdown"
}
//This opens the navbar dropdown
navbarOpenDropdown = () => {
this.setState({
navbarIsActive: "navbar-item has-dropdown is-active"
})
}
//This closes the navbar dropdown
navbarCloseDropdown = () => {
this.setState({
navbarIsActive: "navbar-item has-dropdown"
})
}
render() {
return(
<div>
<nav class="navbar is-transparent" role="navigation" aria-label="dropdown navigation">
<a class="navbar-item">
<h1>Title!</h1>
</a>
<div
class={this.state.navbarIsActive}
onMouseEnter={this.navbarOpenDropdown}
onMouseLeave={this.navbarCloseDropdown}
>
<a class="navbar-link">
Docs
</a>
<div class="navbar-dropdown is-boxed">
<Link to="/">Home</Link>
<Link to="/about">About Us</Link>
<Link to="/blog">Blog</Link>
<hr class="navbar-divider"/>
<div class="navbar-item">
Version 0.7.2
</div>
</div>
</div>
</nav>
<section class="hero">
<div class="hero-body">
<p class="title">
Documentation
</p>
<p class="subtitle">
Everything you need to <strong>create a website</strong> with Bulma
</p>
</div>
</section>
</div>
)
}
}
export default Navbar;

That's just my two pennies' worth: https://nhpcr.codesandbox.io/
src/Navbar.js
import React from 'react';
import PropTypes from 'prop-types';
const NavbarItem = props => (
<a className="navbar-item is-capitalized" href={`#${props.page}`}>
{props.page}
</a>
);
const NavbarBurger = props => (
<button
onClick={props.toggleMenu}
className={`button navbar-burger ${props.active ? 'is-active' : ''}`}
>
<span />
<span />
<span />
</button>
);
export default class Navbar extends React.Component {
state = {
activeMenu: false,
};
toggleMenu = () => {
this.setState({
activeMenu: !this.state.activeMenu,
});
};
render() {
let { pages = [], color } = this.props;
let navbarItems = pages.map(page => <NavbarItem page={page} key={page} />);
return (
<nav className={`navbar is-fixed-top is-${color}`}>
<div className="navbar-brand">
<NavbarItem page="logo" />
<NavbarBurger
active={this.state.activeMenu}
toggleMenu={this.toggleMenu}
/>
</div>
<div
className={`navbar-menu ${this.state.activeMenu ? 'is-active' : ''}`}
>
<div className="navbar-start">{navbarItems}</div>
</div>
</nav>
);
}
}
Navbar.propTypes = {
pages: PropTypes.array.isRequired,
color: PropTypes.string,
};
src/index.js
import React from 'react';
import { render } from 'react-dom';
import Navbar from './Navbar';
import 'bulma/css/bulma.css';
const styles = {
fontFamily: 'sans-serif',
textAlign: 'center',
};
const pages = ['about', 'contact', 'sitemap'];
const App = () => (
<div style={styles}>
<Navbar pages={pages} />
</div>
);
render(<App />, document.getElementById('root'));

Related

toggling class dynamically in react?

I am working on a React application that has a header component with two buttons:
import React, { useRef } from 'react';
import { Link } from 'react-router-dom';
const Header = () => {
return (
<header className='header'>
<h2 className='resume-title'>
Muhammad <br />
Sohaib Furqan
</h2>
<button className='btn'>
<Link to='/'>Home</Link>
</button>
<button className='btn'>
<Link to='/projects'>Projects</Link>
</button>
</header>
);
};
I want to make it so that the button that is clicked gets the 'active' class while the other buttons the 'active' class is removed. In vanillaJS, I would use 'querySelectorAll' to get all buttons and then map through the nodeList, toggling as appropriate. But not sure how I would do that in React. I'm inclined towards useRef but how can I set ref to point to the button that was clicked?
Hope I've managed to make myself clear.
TIA,
Sohaib
See NavLink.
You can use it like
<NavLink className="btn" to='/'>Home</Link>
NavLink will get .active class when path matches. So you can add styles to the active class.
You can also customize the className using
<NavLink to="/" activeClassName="some-custom-class">
Home
</NavLink>
<Link> no longer has the activeClassName or activeStyle properties. In react-router v4 you have to use <NavLink> if you want to do conditional styling:
import React, { useRef } from 'react';
import { NavLink } from 'react-router-dom';
const Header = () => {
return (
<header className='header'>
<h2 className='resume-title'>
Muhammad <br />
Sohaib Furqan
</h2>
<NavLink to='/' className="btn" activeClassName="active">Home</Link>
<NavLink to='/projects' className="btn" activeClassName="active">Projects</Link>
</header>
);
};
and in your style.css
.active { //do-whatever }
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
const Header = () => {
const [active,setActive] = useState("Home")
const handleActive = (activeTab) => {
setActive(activeTab)
}
return (
<header className='header'>
<h2 className='resume-title'>
Muhammad <br />
Sohaib Furqan
</h2>
<button className={active === "Home" ? 'btn active' : 'btn' } onClick={() => handleActive("Home")} >
<Link to='/'>Home</Link>
</button>
<button className={active === "Projects" ? 'btn active' : 'btn' } onClick={() => handleActive("Projects")} >
<Link to='/projects'>Projects</Link>
</button>
</header>
);
};

React JS - How does 2 separated components able to receive 1 same state?

I am a beginner in using the React JS framework. Based on the official React JS documentation, an example is given for changing the state of a component that has a connected hierarchy. But in my case this time I split the components for Header and Main separately.
index.js
ReactDOM.render(
<React.StrictMode>
<Header />
<Main />
</React.StrictMode>,
document.getElementById('root')
);
In the Header component I also have another sub component that functions to activate / deactivate the sidebar which is also a sub menu for the Main component.
Header.js
import { BtnSidebarOnClick } from './Sidebar';
const Header = () => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebarOnClick />
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
export default Header;
Main.js
import { Sidebar } from './Sidebar';
const Main = () => {
return (
<main className="main">
<Sidebar />
<div className="main__content">
...
</div>
</main>
);
}
export default Main;
Notice that the BtnSidebarOnClick and Sidebar components are not connected. In my case, this time I want to make the Sidebar component accept state to detect whether the button contained in the BtnSidebarOnClick component is clicked / not.
Sidebar.js
class BtnSidebarOnClick extends React.Component {
constructor(props) {
super(props);
this.state = { onClick: false };
}
handleClick() {
this.setState(state => ({ onClick: !state.onClick }));
}
render() {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={this.handleClick.bind(this)}>
<i className="material-icons">menu</i>
</div>
</div>
);
}
}
const Sidebar = () => {
return (
<div className="main__sidebar"> {/* set style if BtnSidebarOnClick clicked */}
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}
export { Sidebar, BtnSidebarOnClick };
So how do you set these two components to receive the same state?
TLDR; You should pull out the button state into the parent and pass it into the children component.
By the way, it is a common way to have file App.js for your main Application file. In your case, it should be like this:
index.js
import App from './App';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = { isClicked: false };
}
handleClick() {
this.setState(state => ({ isClicked: !state.isClicked }));
}
render() {
return (
<div>
<Header onClick={this.handleClick} /> // --> notice
<Main isClicked={this.state.isClicked} /> // --> notice
</div>
)
}
}
Header.js
import BtnSidebar from './BtnSidebar';
const Header = (props) => {
return (
<header className="header">
<div className="header__logo">
<BtnSidebar onClick={props.onClick} /> // --> notice
<div className="header__logo_img">
<a className="link"
href="/">
<img src=""
alt="Schedule App" />
</a>
</div>
</div>
<nav className="header__nav">
...
</nav>
</header>
);
}
Main.js
import Sidebar from './Sidebar';
const Main = (props) => {
return (
<main className="main">
<Sidebar isClicked={props.isClicked} /> // --> notice
<div className="main__content">
...
</div>
</main>
);
}
BtnSidebar.js
const BtnSidebar = (props) => {
return (
<div className="header__logo_btn">
<div className="button button--hover button--focus"
role="button"
tabIndex="0"
onClick={props.onClick} // --> notice
>
<i className="material-icons">menu</i>
</div>
</div>
);
}
Sidebar.js
const Sidebar = (props) => {
return (
<div
className={
props.isClicked ? 'main__sidebar-clicked' : 'main__sidebar' // --> notice
}
>
<div className="main__sidebar_menu">
<div className="tag-link">
<a className="link link--hover link--focus link--active"
href="/">
<i className="material-icons">insert_drive_file</i>
<span className="link-title">Files</span>
</a>
</div>
</div>
</div>
);
}

Passing props via <Link> tag to separate page

I am making a react site in which I need to pass some props via a Link tag.
I have set up my link tag and have some props contained within there(aboutProps object).
Here is this code.
<Link
to={{
pathname: "/admin-view-full-user-wsa-responses",
aboutProps: {
WSAId: this.props.WSAId
}
}}
>
<button>View Full Details</button>
</Link>
AS you can see I am trying to pass WSAId through to the component.
This is then the component I am trying to pass this through to
import React from "react";
import logo from "../codestone logo.png";
import NavBar from "../PageDetails/Headers/NavBarUsers";
import LogOutButton from "../PageDetails/Buttons/LogOutButton/LogOutButton";
import ProfileButton from "../PageDetails/Buttons/ProfileButton/ProfileButton";
import AdminButton from "../PageDetails/Buttons/AdminButton/AdminButton";
import { Link } from "react-router-dom";
class Home extends React.Component {
constructor(props) {
super(props);
}
// this console logs as undefined
componentDidMount() {
console.log(this.props.location.aboutProps);
console.log(this.props.location.aboutProps);
}
render() {
return (
<>
<Header />
</>
);
}
}
function Header() {
return (
<div className="jumbotron">
<div style={{ textAlign: "right" }}>
<ProfileButton />
<LogOutButton />
<AdminButton />
</div>
<div className="User-Menu"></div>
<img
className="profile-image"
alt="icon"
src={logo}
width="340"
height="60"
/>
</div>
);
}
export default Home;
Any suggestions on how to resolve this are greatly appreciated.
You should be able to pass them using state instead of aboutProps like so:
<Link
to={{
pathname: "/admin-view-full-user-wsa-responses",
state: {
WSAId: this.props.WSAId
}
}}
>
<button>View Full Details</button>
</Link>
And access it with:
this.props.location.state //in a class component
props.location.state //in a functional component

onClick not invoked when clicking on sidebar

In my main container the layout has this code
import React, { Component } from 'react';
import Header from '../../components/Navigation/Header/Header';
import SideBar from '../../components/Navigation/SideBar/SideBar';
class Layout extends Component {
state = {
showSideBar: true
}
sideBarToggleHandler = () => {
console.log("test");
}
render() {
return (
<div>
<Header />
<div>
<SideBar onClick={this.sideBarToggleHandler}/>
<main id="main">
{this.props.children}
</main>
</div>
</div>
)
}
}
export default Layout;
Whenever I click on any element in the side bar I want to console log test
For some reason this is not happening however if I move the onClick method to the header for example or to main it works fine
This is my sidebar:
import React from 'react';
import classes from './SideBar.module.scss';
import Logo from '../Logo/Logo'
import NavigationItems from './NavigationItems/NavigationItems'
const sideBar = (props) => {
}
return (
<div className={Classes.SideBar}>
<Logo />
<nav>
<NavigationItems />
</nav>
</div>
);
};
export default sideBar;
and this is my navigation items:
import React from 'react';
import classes from './NavigationItems.module.scss';
import Aux from '../../../../hoc/Aux';
import CollapseIcon from '../../../../assets/Images/Icons/collapse.svg'
import { library } from '#fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faHome } from '#fortawesome/free-solid-svg-icons';
import { faFileAlt } from '#fortawesome/free-solid-svg-icons';
import { faChartLine } from '#fortawesome/free-solid-svg-icons';
library.add(faHome);
library.add(faFileAlt);
library.add(faChartLine);
const navigationItems = (props) => {
return (
<Aux>
<p>CONSUMER</p>
<ul className={classes.NavigationItems}>
<li><FontAwesomeIcon className={classes.Icon1Paddig} icon="home" /> Home</li>
<li><FontAwesomeIcon className={classes.Icon2Paddig} icon="file-alt" /> Dashboard</li>
<li><FontAwesomeIcon className={classes.Icon3Paddig} icon="chart-line" /> Statistics</li>
</ul>
<div className={classes.Divider}></div>
<div className={classes.ButtonPosition}>
<button onClick={props.clicked}><img className={classes.CollapseIcon} src={CollapseIcon} alt='icon'></img>Collapse sidebar</button>
</div>
<div className={classes.Divider + ' ' + classes.DividerBottom}></div>
<p className={classes.footer}>Micheal Alfa v 1.0.0</p>
<p className={classes.footer}>Copyrights # 2019 All Rights Reserved</p>
</Aux>
);
};
export default navigationItems;
Any ideas?
Thank you
You are not doing anything with the passed onClick event. You need to put it on something, like so:
const sideBar = (props) => (
<div onClick={this.props.onClick} className={Classes.SideBar}>
<Logo />
<nav>
<NavigationItems />
</nav>
</div>
);
export default sideBar;
This will fire that event when you click on the div. Make sense?

Toggle classes (navbar burger menu [show, hide]) | works in 'develop' but not in 'build'

In the Navbar.js component I want to be able to set state to true or false of the is-active css class, so that when the user presses the burger menu button, the menu shows or hides.
The code below works in the gatsby develop but not in the gatsby build.
There are no errors in 'build'.
Question: Why the code below does not work in gatsby build?
import React from 'react';
import Link from 'gatsby-link';
import logo from '../img/logo.svg';
class Navbar extends React.Component {
state = { showMenu: false }
toggleMenu = () => {
this.setState({
showMenu: !this.state.showMenu
})
}
render() {
const menuActive = this.state.showMenu ? 'is-active' : '';
const burgerActive = this.state.showMenu ? 'is-active' : '';
return (
<nav className="navbar">
<div className="navbar-brand">
<Link className="navbar-item" to="/">
<img src={logo} style={{ width: '88px' }} itemprop="image" alt="" />
</Link>
<div className={`navbar-burger burger ${burgerActive}`} onClick={this.toggleMenu}>
<span></span>
<span></span>
<span></span>
</div>
</div>
<div className={`navbar-menu ${menuActive}`} >
<div className="navbar-start">
<Link className="navbar-link" to="/" onClick={this.toggleMenu}>
Home
</Link>
<Link className="navbar-link" to="/services" onClick={this.toggleMenu}>
Services
</Link>
<Link className="navbar-link" to="/contact" onClick={this.toggleMenu}>
Contact
</Link>
</div>
</div>
</nav>)
}
};
export default Navbar;

Categories

Resources