feat: add toggle nav (#36956)

* feat: re-add toggle menu

* Update client/src/components/Header/components/universalNav.css

Co-Authored-By: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* Update client/src/components/Header/components/universalNav.css

Co-Authored-By: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* Update client/src/components/Header/components/universalNav.css

Co-Authored-By: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>

* fix: fix lint error
This commit is contained in:
Ahmad Abdolsaheb
2019-10-04 18:00:17 +03:00
committed by mrugesh
parent 2066ed674b
commit f9a112b43e
7 changed files with 250 additions and 91 deletions

View File

@ -2,19 +2,17 @@
import React from 'react';
import ShallowRenderer from 'react-test-renderer/shallow';
import TestRenderer from 'react-test-renderer';
import Header from './';
import { UniversalNav } from './components/UniversalNav';
import NavLinks from './components/NavLinks';
describe('<Header />', () => {
describe('<UniversalNav />', () => {
it('renders to the DOM', () => {
const shallow = new ShallowRenderer();
shallow.render(<Header />);
shallow.render(<UniversalNav {...UniversalNavProps} />);
const result = shallow.getRenderOutput();
expect(result).toBeTruthy();
});
});
describe('<NavLinks />', () => {
const root = TestRenderer.create(<NavLinks />).root;
const aTags = root.findAllByType('a');
@ -25,17 +23,23 @@ describe('<NavLinks />', () => {
return acc;
}, []);
const expectedLinks = ['/', '/portfolio'];
const expectedLinks = ['/learn', '/', '/portfolio'];
it('renders to the DOM', () => {
expect(root).toBeTruthy();
});
it('has 3 a tags', () => {
it('has 3 links', () => {
expect(aTags.length === 3).toBeTruthy();
});
it('has link to portfolio', () => {
it('has links to learn, main, and portfolio', () => {
// checks if all links in expected links exist in links
expect(expectedLinks.every(elem => links.indexOf(elem) > -1)).toBeTruthy();
});
});
const UniversalNavProps = {
displayMenu: false,
menuButtonRef: {},
toggleDisplayMenu: function() {}
};

View File

@ -0,0 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
const MenuButton = React.forwardRef((props, ref) => {
return (
<button
aria-expanded={props.displayMenu}
className={
'toggle-button-nav' + (props.displayMenu ? ' reverse-toggle-color' : '')
}
onClick={props.onClick}
ref={ref}
>
Menu
</button>
);
});
MenuButton.displayName = 'MenuButton';
MenuButton.propTypes = {
className: PropTypes.string,
displayMenu: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
};
export default MenuButton;

View File

@ -1,16 +1,25 @@
import React from 'react';
import { Link } from '../../helpers';
export function NavLinks() {
import PropTypes from 'prop-types';
const propTypes = {
displayMenu: PropTypes.bool
};
function NavLinks({ displayMenu }) {
return (
<div className='main-nav-group'>
<ul className={'nav-list display-flex'} role='menu'>
<li className='nav-theme' role='menuitem'>
<Link to='/learn'>Projects</Link>
</li>
<ul
className={'nav-list' + (displayMenu ? ' display-flex' : '')}
role='menu'
>
<li className='nav-theme' role='menuitem'>
<Link to='/'>Light</Link>
</li>
<li className='nav-projects' role='menuitem'>
<Link to='/learn'>Projects</Link>
</li>
<li className='nav-portfolio' role='menuitem'>
<Link to='/portfolio'>Portfolio</Link>
</li>
@ -19,5 +28,6 @@ export function NavLinks() {
);
}
NavLinks.propTypes = propTypes;
NavLinks.displayName = 'NavLinks';
export default NavLinks;

View File

@ -1,27 +1,48 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '../../helpers';
import NavLogo from './NavLogo';
import SearchBar from '../../search/searchBar/SearchBar';
import MenuButton from './MenuButton';
import NavLinks from './NavLinks';
import './universalNav.css';
export function UniversalNav() {
return (
<nav className={'universal-nav nav-padding'} id='universal-nav'>
<div className={'universal-nav-left'}>
export const UniversalNav = React.forwardRef(
({ displayMenu, toggleDisplayMenu }, ref) => (
<nav
className={
'universal-nav nav-padding' + (displayMenu ? ' expand-nav' : '')
}
id='universal-nav'
>
<div
className={'universal-nav-left' + (displayMenu ? ' display-flex' : '')}
>
<SearchBar />
</div>
<div className='universal-nav-middle'>
<Link className='universal-nav-logo' to='/'>
<Link id='universal-nav-logo' to='/'>
<NavLogo />
</Link>
</div>
<div className='universal-nav-right main-nav'>
<NavLinks />
<NavLinks displayMenu={displayMenu} />
</div>
<MenuButton
displayMenu={displayMenu}
onClick={toggleDisplayMenu}
ref={ref}
/>
</nav>
);
}
)
);
UniversalNav.displayName = 'UniversalNav';
export default UniversalNav;
UniversalNav.propTypes = {
displayMenu: PropTypes.bool,
menuButtonRef: PropTypes.object,
toggleDisplayMenu: PropTypes.func
};

View File

@ -7,7 +7,7 @@
height: 40px;
font-size: 18px;
font-family: 'Lato', sans-serif;
height: calc(2 * var(--header-height));
height: var(--header-height);
background: var(--theme-color);
position: absolute;
z-index: 1000;
@ -31,28 +31,25 @@
letter-spacing: 0.4px;
white-space: nowrap;
-ms-overflow-scrolling: touch;
margin-right: 0;
}
.universal-nav-logo {
#universal-nav-logo {
flex-shrink: 0;
display: block;
margin: 0px;
color: var(--gray-00);
font-size: 1.7rem;
line-height: 1em;
font-weight: bold;
letter-spacing: -0.5px;
display: flex;
flex-shrink: 0;
position: absolute;
left: 15px;
top: 0px;
}
.universal-nav-logo:hover {
#universal-nav-logo:hover {
text-decoration: none;
background-color: var(--theme-color);
}
.universal-nav-logo img {
#universal-nav-logo img {
display: block;
width: auto;
height: 25px;
@ -67,7 +64,7 @@
}
.nav-list {
height: var(--header-height);
height: 38px;
}
.nav-list li {
@ -83,7 +80,6 @@
color: var(--gray-00);
opacity: 1;
white-space: nowrap;
height: var(--header-height);
}
.nav-list li:hover {
@ -103,43 +99,115 @@
height: 38px;
}
@media (max-width: 380px) {
.universal-nav {
padding: 0px 5px;
}
.universal-nav-logo {
left: 5px;
}
.toggle-button-nav {
display: none;
padding: 2px 14px 2px;
border: 1px solid var(--gray-00);
font-family: 'lato', sans-serif;
font-size: 18px;
color: var(--gray-00);
outline: none;
background-color: var(--theme-color);
cursor: pointer;
margin-top: 4px;
height: auto;
}
@media (max-width: 265px) {
.nav-list li a {
padding: 8px 5px;
width: 50vw;
}
}
@media (min-width: 700px) {
.universal-nav-logo {
@media (max-width: 799px) {
.site-header {
padding-right: 0;
padding-left: 0;
}
.universal-nav-middle {
margin-right: 0;
}
.universal-nav-left {
display: none;
}
.nav-list {
display: none;
position: absolute;
background-color: var(--theme-color);
top: calc(var(--header-height) * 2);
right: 0px;
flex-wrap: wrap;
justify-content: flex-end;
width: 100vw;
height: auto;
}
.display-flex {
display: flex;
}
.toggle-button-nav {
display: flex;
}
.expand-nav {
min-height: calc(3 * var(--header-height));
}
.reverse-toggle-color {
background-color: var(--gray-00);
color: var(--theme-color);
}
.universal-nav-left form {
display: none;
}
.fcc_searchBar .ais-SearchBox-form {
display: flex;
position: absolute;
top: var(--header-height);
left: 15px;
}
#universal-nav-logo {
display: flex;
position: absolute;
left: 17px;
top: 0px;
}
}
@media (min-width: 800px) {
.universal-nav-middle {
flex: 1 0 30%;
margin-right: 0px;
}
.universal-nav-left {
display: flex;
flex: 1 0 35%;
margin-left: 0px;
}
#universal-nav-logo {
margin-left: auto;
margin-right: auto;
position: static;
left: auto;
top: auto;
}
.universal-nav-right {
flex: 1 0 35%;
margin-left: auto;
}
.main-nav-group {
margin-left: auto;
}
}
@media (min-width: 1500px) {
.universal-nav-middle {
flex: 1 0 33%;
margin-right: 0;
margin-left: 0;
}
.universal-nav-left {
flex: 1 0 33%;
margin-left: 0px;
}
.universal-nav-right {
flex: 1 0 33%;
margin-left: auto;
}
.universal-nav {
height: var(--header-height);
}
}

View File

@ -5,17 +5,56 @@ import UniversalNav from './components/UniversalNav';
import './header.css';
export function Header() {
export class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
displayMenu: false
};
this.menuButtonRef = React.createRef();
this.handleClickOutside = this.handleClickOutside.bind(this);
this.toggleDisplayMenu = this.toggleDisplayMenu.bind(this);
}
componentDidMount() {
document.addEventListener('click', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside);
}
handleClickOutside(event) {
if (
this.state.displayMenu &&
this.menuButtonRef.current &&
!this.menuButtonRef.current.contains(event.target) &&
event.target.id !== 'fcc_instantsearch'
) {
this.toggleDisplayMenu();
}
}
toggleDisplayMenu() {
this.setState(({ displayMenu }) => ({ displayMenu: !displayMenu }));
}
render() {
const { displayMenu } = this.state;
return (
<>
<Helmet>
<style>{':root{--header-height: 38px}'}</style>
</Helmet>
<header>
<UniversalNav />
<UniversalNav
displayMenu={displayMenu}
ref={this.menuButtonRef}
toggleDisplayMenu={this.toggleDisplayMenu}
/>
</header>
</>
);
}
}
Header.displayName = 'Header';

View File

@ -29,6 +29,12 @@
color: var(--gray-00);
}
.fcc_searchBar .ais-Hits {
top: 71px;
width: calc(100vw - 30px);
left: 15px;
}
.fcc_searchBar .ais-SearchBox-form {
margin-bottom: 0;
display: flex;
@ -37,12 +43,6 @@
right: 5px;
}
.fcc_searchBar .ais-Hits {
top: 71px;
width: calc(100vw - 10px);
left: 5px;
}
/* hits */
.fcc_searchBar .ais-Highlight-highlighted {
background-color: transparent;
@ -103,23 +103,18 @@
font-weight: bold;
}
@media (min-width: 380px) {
.fcc_searchBar .ais-Hits {
.ais-SearchBox-input {
width: calc(100vw - 30px);
left: 15px;
}
.ais-SearchBox-input {
width: calc(100vw - 30px);
}
.fcc_searchBar .ais-SearchBox-form {
}
.fcc_searchBar .ais-SearchBox-form {
display: flex;
position: absolute;
top: var(--header-height);
right: 15px;
}
}
@media (min-width: 700px) {
@media (min-width: 800px) {
.ais-SearchBox-input {
width: 100%;
margin-top: 6px;
@ -130,7 +125,8 @@
}
.fcc_searchBar .ais-Hits {
top: auto;
width: calc(80vw - 20px);
width: calc(100% - 20px);
max-width: 500px;
left: 10px;
}
.fcc_searchBar .ais-SearchBox-form {
@ -143,8 +139,3 @@
left: 0.85rem;
}
}
@media (min-width: 1100px) {
.fcc_searchBar .ais-Hits {
width: calc(100% - 20px);
}
}