React Navbar active style state menu Link ReactJs - reactjs

How can I make on clicking a specific menuItem to navigate to the component in https://codepen.io/mrhamburger/pen/XzjXGb.
I have used similar example But did i little of change because I am using Link imported from NextJs, But the problem is The Link itself and the active style are not connected, my problem is I Have menuItems as state and want to make the active state have a specific active design highlight (menu-item--active) when I click the box or div that containes the Link.
import React, { Component } from 'react';
import Link from 'next/link';
// import MenuItem from './MenuItem';
import '../style.scss';
class Sidebar extends React.Component {
constructor(props) {
super(props)
this.state = {
activeItem: '',
activeItemPosition: 0,
activeItemColor: '',
menuItems: [
{ text: 'ComponentA' },
{ text: 'ComponentB' },
{ text: 'ComponentC' },
{ text: 'ComponentD' },
{ text: 'ComponentE' }
],
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(activeItem) {
return e => {
e.preventDefault()
this.setState({
activeItem,
activeItemPosition: document.getElementById(activeItem).offsetTop,
activeItemColor: window.getComputedStyle(document.getElementById(activeItem)).getPropertyValue('background-color'),
})
}
}
render() {
const menuItems = this.state.menuItems.map(item => <MenuItem item={ item } handleClick={ this.handleClick }/>)
return (
<div className='menu-container'>
<span className='menu-item--active' style={{ top: this.state.activeItemPosition, backgroundColor: this.state.activeItemColor }} />
<div id="ComponentA" className="menu-item" onClick={ this.handleClick('ComponentA') } >
<Link href="/ComponentA" >
ComponentA
</Link>
</div>
<div id="ComponentB" className="menu-item" onClick={ this.handleClick('ComponentB') }>
<Link href="/ComponentB">
ComponentB
</Link></div>
<div id="ComponentC" className="menu-item" onClick={ this.handleClick('ComponentC') } >
<Link href="/ComponentC">
ComponentC
</Link>
</div>
<div id="ComponentD" className="menu-item" onClick={ this.handleClick('ComponentD') }>
<Link href="/ComponentD">
ComponentD
</Link></div>
<div id="ComponentE" className="menu-item" onClick={ this.handleClick('ComponentE') } >
<Link href="/ComponentE">
ComponentE
</Link>
</div>
{/* { menuItems } */}
</div>
)
}
}
function MenuItem(props) {
return (
<div>
</div>
)
}
export default Sidebar;
So, my problem is when I click on any item on the menu, if I clicked the text it shows the component but the active still doesn't highlight as active item unless i click the item itself without the link or text.
Please Help, I want to be able to click on any item so it stays highlighted as active style and shows the right component.
Thanks in advance.

Related

React SlidingPane header getting hidden under Nav bar

This is my App.js:
export class App extends React.Component {
render() {
return (
<BrowserRouter>
<NavigationBar />
<Routes />
</BrowserRouter>
);
}
}
And this is where I am using a SlidingPane on a button click:
Product.js:
class App extends Component {
constructor(props) {
super(props);
this.state = {
isPaneOpen: false,
isPaneOpenLeft: false
};
}
render() {
return <div>
<button onClick={() => this.setState({ isPaneOpen: true })}>Click me to open right pane!</button>
<div style={{ marginTop: '32px' }}>
<button onClick={ () => this.setState({ isPaneOpenLeft: true }) }>
Click me to open left pane with 20% width!
</button>
</div>
<SlidingPane
className='some-custom-class'
overlayClassName='some-custom-overlay-class'
isOpen={ this.state.isPaneOpen }
title='Hey, it is optional pane title. I can be React component too.'
subtitle='Optional subtitle.'
onRequestClose={ () => {
// triggered on "<" on left top click or on outside click
this.setState({ isPaneOpen: false });
} }>
<div>And I am pane content. BTW, what rocks?</div>
<br />
<img src='img.png' />
</SlidingPane>
</div>;
}
}
render(<App />, document.getElementById('app'));
Apparently, the Slidingpane header is getting hidden under the NavBar. When I remove the navbar, I can see the SlidingPane header, but when I add it, it is getting display beneath it. How do I make my Pane to be independent of the NavBar?
You can increase CSS z-index of Slidingpane. If needed you should set position: "relative" If you are writing inline styles you can set it like
style={{zIndex: "10", position: "relative"}}

React Navbar Not Collapsing On Link Click

I've created a React NavBar following a tutorial, when I click the burger menu, the nav expands and collapses as expected, but when I click a link on the nav menu, it goes to the page but the nav bar doesn't collapse. I've checked a few questions/guides but they all link to Bootstrap and this code doesn't use Bootstrap, I'd rather not change the NavBar to Bootstrap if it can be avoided! Any help would be appreciated.
import React, { Component } from "react";
import logo from "../images/logo.svg";
import { FaAlignRight } from "react-icons/fa";
import { Link } from "react-router-dom";
export default class Navbar extends Component {
state = {
isOpen: false
};
handleToggle = () => {
this.setState({ isOpen: !this.state.isOpen });
};
componentDidMount() {
window.addEventListener("scroll", this.resizeHeaderOnScroll);
window.addEventListener("scroll", this.navTransparent);
window.addEventListener("scroll", this.navShadow);
};
resizeHeaderOnScroll() {
const distanceY = window.pageYOffset || document.documentElement.scrollTop,
shrinkOn = 100,
headerEl = document.getElementById("logo");
if (distanceY > shrinkOn) {
headerEl.classList.add("logoShrink");
} else {
headerEl.classList.remove("logoShrink");
}
}
navTransparent() {
const distanceY = window.pageYOffset || document.documentElement.scrollTop,
shrinkOn = 100,
headerEl = document.getElementById("navbar");
if (distanceY > shrinkOn) {
headerEl.classList.add("navbarBg");
} else {
headerEl.classList.remove("navbarBg");
}
}
navShadow() {
const distanceY = window.pageYOffset || document.documentElement.scrollTop,
shrinkOn = 100,
headerEl = document.getElementById("navbar");
if (distanceY > shrinkOn) {
headerEl.classList.add("navShadow");
} else {
headerEl.classList.remove("navShadow");
}
}
render() {
return <nav id="navbar">
<div className="nav-center">
<div className="nav-header">
<Link to="/">
<img id="logo" src={logo} alt="" />
</Link>
<button type="button" className="nav-btn" onClick={this.handleToggle}>
<FaAlignRight className="nav-icon" />
</button>
</div>
<ul className={this.state.isOpen ? "nav-links show-nav" : "nav-links"}>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/nigelservices">Services</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
</div>
</nav>;
}
}
To answer the question here you can always do this because the Link component accepts the onClick prop:
export default class Navbar extends Component {
// Rest of your code
handleLinkClick = () => {
this.setState({ isOpen: false });
};
render() {
return (
// Your JSX
<Link to="/" onClick={handleLinkClick}>Home</Link>
)
}
}
Remember to add this in every link component.
As a side note you can also use the NavLink component in react router to handle the styling when the route is the current one. https://reactrouter.com/web/api/NavLink

Rendering a Bootstrap Component Using JSX

Editing for clarity: I cannot figure out how to dynamically create Boostrap Components using JSX in a react app. End goal is to get the new button in the "newBtnSpace" div when the first button is clicked. I have tried using show.hide methods, but those need to be hard coded. Trying to create buttons based off an array. code:
./components/newBSBtnSpaceFunc.js
import React, { Component } from 'react'
import { Button } from 'reactstrap'
export default function NewBSBtnFunc() {
let BtnArray = ["red", "blue", "green"].map((btn) => {
return React.createElement(
Button,
{variant: 'primary'},
'New Button',
{id: "newBtn"},
btn
)
}
./components/BSBtn.js
import React, { Component } from 'react'
import { Button } from 'reactstrap'
import NewBSBtnFunc from "./NewBSBtnFunc"
export default class BSBtn extends Component {
render() {
return (
<div>
<Button onClick={NewBSBtnFunc}>Click Me</Button>
<div id="newBtnSpace"></div>
</div>
)
}
}
App.js
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import BSBtn from "./components/BSBtn"
function App() {
return (
<div>
<BSBtn></BSBtn>
</div>
);
}
export default App;
github link: https://github.com/mollygilbert389/testingBootstrapBtn
You can conditionally show the new button by setting a state item (in this case showNewButton) to true in the onClick of the original button.
render() {
return (
<div>
<Button onClick={() => this.setState({ showNewButton: true }))}>Click Me</Button>
<div id="newBtnSpace">{ this.state.showNewButton && <Button variant="primary" id="newBtn">New Button</Button> }</div>
</div>
)
}
PS you've already successfully worked out how to create Bootstrap buttons in jsx:
<Button onClick={NewBSBtnFunc}>Click Me</Button>
onClick does not expect a return value so returning the new button won't do anything.
The way you have things organized makes it very difficult since you can't return anything from the function, and you can't modify state from outside the class. I would suggest moving your click handler into the component and using to to modify a state value that will show the second button.
Here is my suggestion:
import React, { Component } from 'react'
import { Button } from 'reactstrap'
export default class BSBtn extends Component {
state = {show: false}
handleClick = () => {
this.setState({ show: !this.state.show })
}
render() {
return (
<div>
<Button onClick={this.handleClick}>Click Me</Button>
<div id="newBtnSpace">
{this.state.show ?
<Button variant="primary" id="newBtn">New Button</Button>
: null}
</div>
</div>
)
}
}
Updated solution to your updated question:
class BSBtn extends React.Component {
state = {
show: false,
buttons: []
}
handleClick = () => {
this.setState({ show: !this.state.show })
}
handleAdd = () => {
this.setState({ buttons: [...this.state.buttons, (this.state.buttons.length + 1)] })
}
render() {
return (
<div>
<h3>Option 1</h3>
<button onClick={this.handleClick}>Click Me</button>
<div id="newBtnSpace">
{this.state.show ? [1,2,3].map((value) => (
<div>
<button>Button {value}</button>
</div>
))
: null}
</div>
<hr/>
<div style={{ marginTop: '30px' }}>
<h3>Option 2</h3>
<button onClick={this.handleAdd}>Click Me</button>
{this.state.buttons.map((value) => (
<div>
<button>Button {value}</button>
</div>
))}
</div>
</div>
)
}
}
ReactDOM.render(<BSBtn />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root' />

How to close sidebar when clicking link?

I am trying to get my sidebar to close when I click on any of the menu options. I was able to get the sidebar to close/open whenever I click on the burger icon, but not sure if I am supposed to make my sidebar component a class and have its own state. Below are my navigation and sidebar components.
import React from 'react';
import { Link } from 'react-router-dom';
import { ReactComponent as MenuIcon } from '../../assets/menu.svg';
import { ReactComponent as CloseIcon } from '../../assets/x-mark.svg';
import './navigation.styles.scss';
import Sidebar from '../sidebar/sidebar.component';
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
isSidebarHidden: true
};
this.handleSidebar = this.handleSidebar.bind(this);
}
handleSidebar() {
this.setState({ isSidebarHidden: !this.state.isSidebarHidden });
}
render() {
const { isSidebarHidden } = this.state;
return (
<div className='navigation'>
<div className='logo-container'>
<Link className='logo' to='/'>
NAME
</Link>
</div>
<div className='navigation-options'>
<Link className='option' to='/projects'>
PROJECTS
</Link>
<Link className='option' to='contact'>
CONTACT
</Link>
{isSidebarHidden ? (
<MenuIcon className='menu-icon' onClick={this.handleSidebar} />
) : (
<CloseIcon className='menu-icon' onClick={this.handleSidebar} />
)}
</div>
{isSidebarHidden ? null : <Sidebar />}
</div>
);
}
}
export default Navigation;
import React from 'react';
import { Link } from 'react-router-dom';
import './sidebar.styles.scss';
const Sidebar = () => (
<div className='sidebar'>
<Link className='sidebar-option' to='/projects'>
PROJECS
</Link>
<Link className='sidebar-option' to='/contact'>
CONTACT
</Link>
</div>
);
export default Sidebar;
You could create a method to hide the sidebar and pass it to the Sidebar component, so it executes when you click the links.
const Sidebar = ({hideSidebar}) => (
<div className='sidebar'>
<Link onClick={hideSidebar} className='sidebar-option' to='/projects'>
PROJECS
</Link>
<Link onClick={hideSidebar} className='sidebar-option' to='/contact'>
CONTACT
</Link>
</div>
);
Or you could also execute it every time you move to a different path listening to the browser history with react-router.
import { browserHistory } from 'react-router';
browserHistory.listen(handleRouteChange);
I suggest controlling the component with props instead of using if statement inside the parent component.
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import './sidebar.styles.scss';
const Sidebar = ({ visibility, setVisibility }) => {
if (visibility) {
return (
<div className='sidebar'>
<Link className='sidebar-option' to='/projects' onClick={() => setVisibility()}>
PROJECS
</Link>
<Link className='sidebar-option' to='/contact' onClick={() => setVisibility()}>
CONTACT
</Link>
</div>
)
}
return null
};
export default Sidebar;
As you see, I passed setVisibility prop to onClick callback on the sidebar links and checked if visibility is true then return the sidebar contents. So in this step, we just need to pass this.handleSidebar to setVisibility prop and the parent state isSidebarHidden to the visibility prop.
import React from 'react';
import { Link } from 'react-router-dom';
import { ReactComponent as MenuIcon } from '../../assets/menu.svg';
import { ReactComponent as CloseIcon } from '../../assets/x-mark.svg';
import './navigation.styles.scss';
import Sidebar from '../sidebar/sidebar.component';
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = { isSidebarHidden: true };
this.handleSidebar = this.handleSidebar.bind(this);
}
handleSidebar() {
this.setState({ isSidebarHidden: !this.state.isSidebarHidden });
}
render() {
const { isSidebarHidden } = this.state;
return (
<div className='navigation'>
<div className='logo-container'>
<Link className='logo' to='/'>
NAME
</Link>
</div>
<div className='navigation-options'>
<Link className='option' to='/projects'>
PROJECTS
</Link>
<Link className='option' to='contact'>
CONTACT
</Link>
{isSidebarHidden ? (
<MenuIcon className='menu-icon' onClick={this.handleSidebar} />
) : (
<CloseIcon className='menu-icon' onClick={this.handleSidebar} />
)}
</div>
<Sidebar visibility={isSidebarHidden} setVisibility={this.handleSidebar} />
</div>
);
}
}
export default Navigation;
Then it works.
For the people using bootstrap offcanvas as a sidebar there is a very easy way to do it using only bootstrap and with no JavaScript.
<li data-bs-dismiss="offcanvas">Skills</li>
The above code represent li as one of the item in the sidebar and on upon clicking it takes you to skill section and also closes as it is in dismiss state.

Trigger a function to close a modal window in ReactJS

I'm creation my portfolio section in ReactJS with Gatsby and I have some trouble to handle KeyDown.
My code permits me to detect when I'm pressing on ESC key but I can't trigger the close function as I did for the overlay (onClick event).
I have three differents files :
modal.js - Modal component
project.js - Project component
projets.js - project page
I create the modal window which will display the details of the project. The project component will display all the project thumbnails and finally the project page will render the Project Component.
Maybe there's something that I missing. I will appreciate your help.
Here is the code for the modal component :
modal.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Link } from 'gatsby-link'
import './modal.scss'
import MdClose from 'react-icons/lib/md/close'
export class Modal extends Component {
constructor(props) {
super(props)
}
componentDidMount() {
this.initializeEscClosing();
}
initializeEscClosing() {
if (typeof window !== 'undefined') {
window.addEventListener('keydown', (e) => {
if (e.which == 27) {
//this.props.onClose
console.log('It\'s working')
}
});
}
}
render() {
// Render nothing if the "show" prop is false
if (!this.props.show) {
return null;
}
return (
<div className={`modal`}>
<div className={`modal__overlay`}
onClick={this.props.onClose}
onKeyDown = {
this.initializeEscClosing
}
tabIndex = "0"
>
</div>
<div className={`modal__container`}>
<div className={`modal__body`}>
<div className={`top`}>
<button onClick={this.props.onClose}><MdClose /></button>
</div>
<div className={`content`}>
{this.props.children}
</div>
</div>
</div>
</div>
)
}
}
Modal.propTypes = {
onClose: PropTypes.func.isRequired,
show: PropTypes.bool,
children: PropTypes.node
};
export default Modal
I noticed that when I'm pressing on ESC, the function is triggered 3 times because I have 3 projects in my .json file. How can I fix this issue ?
Here is the code for the project component :
project.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {Link} from 'gatsby-link'
import './project.scss'
import {LinkWebsite, ButtonProject} from '../../../components/atoms/button'
import {Modal } from '../modal'
export class Project extends Component {
constructor(props){
super(props)
this.state = {
opened:false
}
this._toggleModal = this._toggleModal.bind(this)
}
_toggleModal(){
this.setState({
opened: !this.state.opened
})
}
render(){
const { title, category, image, logo, children, website} = this.props
return(
<div className="project__container">
<div className="project__preview">
<button onClick={this._toggleModal}>
{logo ? <img src={logo.src} alt={title} /> : null}
<h2>{title} <span className="category">{category}</span></h2>
</button>
</div>
<div className="project__details">
<Modal
onClose={this._toggleModal}
show={this.state.opened}
>
{image ? <img src={image.src} alt={title} /> : null}
<h3>{title} <span className="category">{category}</span></h3>
{children}
{website ? <LinkWebsite link={website}>Voir le site</LinkWebsite> : null}
</Modal>
</div>
</div>
)
}
}
export default Project
Project.propTypes = {
title: PropTypes.string.isRequired,
category: PropTypes.string.isRequired,
image: PropTypes.shape({
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
}).isRequired,
logo: PropTypes.shape({
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
}).isRequired,
children: PropTypes.element.isRequired,
website: PropTypes.string,
};
Project.defaultProps = {
title: 'Nom du projet',
image: null,
logo: null,
children: 'Texte introductif du projet. Il fourni les éléments clés',
website: null,
};
Here is the code for the project page :
projets.js
import React, { Component } from 'react'
import Link from 'gatsby-link'
import { Project } from '../components/molecules/project'
const projectPage = ({ data }) => {
return(
<div>
<h1>Projets récents</h1>
<div className="projects__container">
{data.allProjectsJson.edges.map(({ node }, i) =>
(<Project
key={i}
title={node.title}
category={node.category}
image={{
src: node.image.childImageSharp.original.src,
alt: node.title,
}}
logo={{
src: node.logo.childImageSharp.original.src,
alt: node.title,
}}
website={node.website}
>
<p dangerouslySetInnerHTML={{ __html: node.description }} />
</Project>),
)}
</div>
</div>
)
}
export default projectPage
export const pageQuery = graphql`
query ProjectsQuery {
allProjectsJson {
edges {
node {
title
category
description
image {
childImageSharp {
original {
src
}
}
}
logo {
childImageSharp {
original {
src
}
}
}
website
}
}
}
}
`;
Thank you in advance for taking the time to help
Wish you a great Friday,
Kind regards,
Maral
I think that there is something not completely correct in the initializeEscClosing() function.
Why you create a listener inside it ? onKeyDown is a listener itselfs.
Try to do something like this to trigger the event.
<div onKeyDown = {event => this.keyDownHandler(event)}/>
event prop has all the infos about the key event so in the function that handles it you could check if the key is the correct one and eventually close the modal
You could try using arrow functions, I don’t have a laptop handy, apologize for that, but you can try something like this
// Modal component
...
onClick={()=> {this.props.onClose()}
// Project component
onClose={() => {console.log(‘might be triggered’); this.toggleModal()}}
Using arrow functions gets rid you of binding the functions from constructors
source

Resources