fix(client): make top navigation menu replaceable (#35943)
* fix(client): Stop using react-responsive and use media queries to display menu * Change guide to show menu * DRYed out a bit * Restore main, top-right nav to guide * fix: Separate guide and top menu state * Update client/src/components/Header/index.js Co-Authored-By: Valeriy <ValeraS@users.noreply.github.com> * Update client/src/components/Header/index.js Co-Authored-By: Valeriy <ValeraS@users.noreply.github.com> * Update client/src/components/Header/index.js Co-Authored-By: Valeriy <ValeraS@users.noreply.github.com> * Update client/src/components/Header/index.js Co-Authored-By: Valeriy <ValeraS@users.noreply.github.com> * fix: Refactor menu button and links * feat(client): make top navigation menu replaceable * fix: Refactor nav menu logic out of Header * fix(client): use default nav menu in header and use landingPage props instead of disableSettings
This commit is contained in:
committed by
Valeriy
parent
02427ad982
commit
17e112d25e
@ -10,6 +10,7 @@ import {
|
|||||||
DefaultLayout,
|
DefaultLayout,
|
||||||
GuideLayout
|
GuideLayout
|
||||||
} from './src/components/layouts';
|
} from './src/components/layouts';
|
||||||
|
import GuideNavMenu from './src/components/layouts/components/guide/NavMenu';
|
||||||
|
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ export const wrapPageElement = ({ element, props }) => {
|
|||||||
}
|
}
|
||||||
if (/^\/guide(\/.*)*/.test(pathname)) {
|
if (/^\/guide(\/.*)*/.test(pathname)) {
|
||||||
return (
|
return (
|
||||||
<DefaultLayout disableMenuButtonBehavior={true} mediaBreakpoint='991px'>
|
<DefaultLayout navigationMenu={<GuideNavMenu />}>
|
||||||
<GuideLayout>{element}</GuideLayout>
|
<GuideLayout>{element}</GuideLayout>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
);
|
);
|
||||||
|
@ -5,12 +5,9 @@ import { Provider } from 'react-redux';
|
|||||||
|
|
||||||
import headComponents from './src/head';
|
import headComponents from './src/head';
|
||||||
import { createStore } from './src/redux/createStore';
|
import { createStore } from './src/redux/createStore';
|
||||||
|
import { wrapPageElement } from './gatsby-browser';
|
||||||
|
|
||||||
import {
|
export { wrapPageElement };
|
||||||
CertificationLayout,
|
|
||||||
DefaultLayout,
|
|
||||||
GuideLayout
|
|
||||||
} from './src/components/layouts';
|
|
||||||
|
|
||||||
const store = createStore();
|
const store = createStore();
|
||||||
|
|
||||||
@ -22,39 +19,6 @@ wrapRootElement.propTypes = {
|
|||||||
element: PropTypes.any
|
element: PropTypes.any
|
||||||
};
|
};
|
||||||
|
|
||||||
export const wrapPageElement = ({ element, props }) => {
|
|
||||||
const {
|
|
||||||
location: { pathname }
|
|
||||||
} = props;
|
|
||||||
if (pathname === '/') {
|
|
||||||
return (
|
|
||||||
<DefaultLayout disableSettings={true} landingPage={true}>
|
|
||||||
{element}
|
|
||||||
</DefaultLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (/^\/certification(\/.*)*/.test(pathname)) {
|
|
||||||
return <CertificationLayout>{element}</CertificationLayout>;
|
|
||||||
}
|
|
||||||
if (/^\/guide(\/.*)*/.test(pathname)) {
|
|
||||||
return (
|
|
||||||
<DefaultLayout onGuide={true}>
|
|
||||||
<GuideLayout>{element}</GuideLayout>
|
|
||||||
</DefaultLayout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (/^\/learn(\/.*)*/.test(pathname)) {
|
|
||||||
return <DefaultLayout showFooter={false}>{element}</DefaultLayout>;
|
|
||||||
}
|
|
||||||
return <DefaultLayout>{element}</DefaultLayout>;
|
|
||||||
};
|
|
||||||
|
|
||||||
wrapPageElement.propTypes = {
|
|
||||||
element: PropTypes.any,
|
|
||||||
location: PropTypes.objectOf({ pathname: PropTypes.string }),
|
|
||||||
props: PropTypes.any
|
|
||||||
};
|
|
||||||
|
|
||||||
export const onRenderBody = ({ setHeadComponents, setPostBodyComponents }) => {
|
export const onRenderBody = ({ setHeadComponents, setPostBodyComponents }) => {
|
||||||
setHeadComponents([...headComponents]);
|
setHeadComponents([...headComponents]);
|
||||||
setPostBodyComponents(
|
setPostBodyComponents(
|
||||||
|
29
client/src/components/Header/components/MenuButton.js
Normal file
29
client/src/components/Header/components/MenuButton.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import './menuButton.css';
|
||||||
|
|
||||||
|
const MenuButton = React.forwardRef((props, ref) => (
|
||||||
|
<button
|
||||||
|
aria-expanded={props.displayMenu}
|
||||||
|
className={
|
||||||
|
'menu-button' +
|
||||||
|
(props.displayMenu ? ' menu-button-open' : '') +
|
||||||
|
' ' +
|
||||||
|
(props.className ? props.className : 'top-menu-button')
|
||||||
|
}
|
||||||
|
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;
|
40
client/src/components/Header/components/MenuLinks.js
Normal file
40
client/src/components/Header/components/MenuLinks.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import { Link } from '../../helpers';
|
||||||
|
import UserState from '../components/UserState';
|
||||||
|
|
||||||
|
import './menuLinks.css';
|
||||||
|
|
||||||
|
function MenuLinks(props) {
|
||||||
|
return (
|
||||||
|
<ul className={props.className} id='top-right-nav'>
|
||||||
|
<li>
|
||||||
|
<Link className='top-right-nav-link' to='/learn'>
|
||||||
|
Learn
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link className='top-right-nav-link' external={true} to='/forum'>
|
||||||
|
Forum
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link className='top-right-nav-link' external={true} to='/news'>
|
||||||
|
News
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<UserState disableSettings={props.disableSettings} />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
MenuLinks.displayName = 'MenuLinks';
|
||||||
|
MenuLinks.propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
disableSettings: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
export default MenuLinks;
|
65
client/src/components/Header/components/NavMenu.js
Normal file
65
client/src/components/Header/components/NavMenu.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
import MenuButton from './MenuButton';
|
||||||
|
import MenuLinks from './MenuLinks';
|
||||||
|
|
||||||
|
class NavigationMenu extends 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)
|
||||||
|
) {
|
||||||
|
this.toggleDisplayMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleDisplayMenu() {
|
||||||
|
this.setState(({ displayMenu }) => ({ displayMenu: !displayMenu }));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { disableSettings } = this.props;
|
||||||
|
const { displayMenu } = this.state;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuButton
|
||||||
|
displayMenu={displayMenu}
|
||||||
|
onClick={this.toggleDisplayMenu}
|
||||||
|
ref={this.menuButtonRef}
|
||||||
|
/>
|
||||||
|
<MenuLinks
|
||||||
|
className={'top-nav' + (displayMenu ? ' top-nav-expanded' : '')}
|
||||||
|
disableSettings={disableSettings}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationMenu.displayName = 'NavigationMenu';
|
||||||
|
NavigationMenu.propTypes = {
|
||||||
|
disableSettings: PropTypes.bool
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NavigationMenu;
|
26
client/src/components/Header/components/menuButton.css
Normal file
26
client/src/components/Header/components/menuButton.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
#top-nav .menu-button {
|
||||||
|
color: white;
|
||||||
|
background-color: transparent;
|
||||||
|
margin: 0 20px 0 12px;
|
||||||
|
padding: 2px 14px;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-nav .menu-button-open {
|
||||||
|
background: white;
|
||||||
|
color: #006400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 735px) {
|
||||||
|
#top-nav .top-menu-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 420px) {
|
||||||
|
#top-nav .menu-button {
|
||||||
|
margin: 0 10px 0 4px;
|
||||||
|
}
|
||||||
|
}
|
62
client/src/components/Header/components/menuLinks.css
Normal file
62
client/src/components/Header/components/menuLinks.css
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#top-right-nav {
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #006400;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-right-nav li {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-right-nav-link {
|
||||||
|
max-height: var(--header-height);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 8px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-right-nav-link:hover,
|
||||||
|
.top-right-nav-link:focus,
|
||||||
|
.top-right-nav-link:active {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #006400;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-state-spinner {
|
||||||
|
height: var(--header-height);
|
||||||
|
padding: 0 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-state-spinner > div {
|
||||||
|
animation-duration: 1.5s !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-right-nav .signup-btn,
|
||||||
|
#top-right-nav .settings-link {
|
||||||
|
margin: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-right-nav .user-avatar {
|
||||||
|
max-height: calc(var(--header-height) - 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 734px) {
|
||||||
|
#top-right-nav {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--header-height);
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100vw;
|
||||||
|
height: min-content;
|
||||||
|
min-height: 160px;
|
||||||
|
padding: 10px 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-right-nav.top-nav-expanded {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
@ -26,49 +26,6 @@ header {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#top-nav .nav-logo {
|
|
||||||
max-height: 25px;
|
|
||||||
min-width: 35px;
|
|
||||||
margin: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-right-nav {
|
|
||||||
display: flex;
|
|
||||||
margin: 0;
|
|
||||||
list-style: none;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #006400;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-right-nav li {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-right-nav-link {
|
|
||||||
max-height: var(--header-height);
|
|
||||||
color: #fff;
|
|
||||||
font-size: 18px;
|
|
||||||
padding: 8px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-right-nav-link:hover,
|
|
||||||
.top-right-nav-link:focus,
|
|
||||||
.top-right-nav-link:active {
|
|
||||||
background-color: #fff;
|
|
||||||
color: #006400;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-state-spinner {
|
|
||||||
height: var(--header-height);
|
|
||||||
padding: 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-state-spinner > div {
|
|
||||||
animation-duration: 1.5s !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search bar */
|
/* Search bar */
|
||||||
.fcc_searchBar {
|
.fcc_searchBar {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -127,6 +84,12 @@ header {
|
|||||||
|
|
||||||
/* Navbar logo */
|
/* Navbar logo */
|
||||||
|
|
||||||
|
#top-nav .nav-logo {
|
||||||
|
max-height: 25px;
|
||||||
|
min-width: 35px;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.logoContainer {
|
.logoContainer {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
@ -138,44 +101,11 @@ header {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#top-nav .menu-button {
|
|
||||||
color: white;
|
|
||||||
background-color: transparent;
|
|
||||||
margin: 0 20px 0 12px;
|
|
||||||
padding: 2px 14px;
|
|
||||||
border: 1px solid #fff;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-nav .menu-button-open {
|
|
||||||
background: white;
|
|
||||||
color: #006400;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-right-nav .signup-btn,
|
|
||||||
#top-right-nav .settings-link {
|
|
||||||
margin: 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-right-nav .user-avatar {
|
|
||||||
max-height: calc(var(--header-height) - 4px);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 734px) {
|
@media (max-width: 734px) {
|
||||||
#top-nav {
|
#top-nav {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#top-right-nav {
|
|
||||||
position: absolute;
|
|
||||||
top: var(--header-height);
|
|
||||||
flex-direction: column;
|
|
||||||
width: 100vw;
|
|
||||||
height: min-content;
|
|
||||||
min-height: 160px;
|
|
||||||
padding: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#top-nav .nav-logo {
|
#top-nav .nav-logo {
|
||||||
margin: 0 0 0 10px;
|
margin: 0 0 0 10px;
|
||||||
}
|
}
|
||||||
@ -185,14 +115,4 @@ header {
|
|||||||
#top-nav .nav-logo {
|
#top-nav .nav-logo {
|
||||||
margin: 0 0 0 5px;
|
margin: 0 0 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#top-nav .menu-button {
|
|
||||||
margin: 0 10px 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ais-Hits {
|
|
||||||
background-color: #fff;
|
|
||||||
left: 0.5em;
|
|
||||||
top: 2.4em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,20 @@
|
|||||||
import React, { Component } from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { bindActionCreators } from 'redux';
|
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import Media from 'react-responsive';
|
|
||||||
import FCCSearch from 'react-freecodecamp-search';
|
import FCCSearch from 'react-freecodecamp-search';
|
||||||
|
|
||||||
|
import NavigationMenu from './components/NavMenu';
|
||||||
import NavLogo from './components/NavLogo';
|
import NavLogo from './components/NavLogo';
|
||||||
import UserState from './components/UserState';
|
|
||||||
import { Link } from '../helpers';
|
import { Link } from '../helpers';
|
||||||
|
|
||||||
import './header.css';
|
import './header.css';
|
||||||
|
|
||||||
import {
|
|
||||||
toggleDisplayMenu,
|
|
||||||
displayMenuSelector
|
|
||||||
} from '../layouts/components/guide/redux';
|
|
||||||
|
|
||||||
const mapStateToProps = createSelector(
|
|
||||||
displayMenuSelector,
|
|
||||||
displayMenu => ({
|
|
||||||
displayMenu
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch =>
|
|
||||||
bindActionCreators({ toggleDisplayMenu }, dispatch);
|
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
disableMenuButtonBehavior: PropTypes.bool,
|
|
||||||
disableSettings: PropTypes.bool,
|
disableSettings: PropTypes.bool,
|
||||||
displayMenu: PropTypes.bool,
|
navigationMenu: PropTypes.element
|
||||||
mediaBreakpoint: PropTypes.string.isRequired,
|
|
||||||
toggleDisplayMenu: PropTypes.func.isRequired
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Header extends Component {
|
function Header(props) {
|
||||||
constructor(props) {
|
const { disableSettings, navigationMenu } = props;
|
||||||
super(props);
|
|
||||||
this.menuButtonRef = React.createRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
document.addEventListener('click', this.handleClickOutside);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
document.removeEventListener('click', this.handleClickOutside);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClickOutside = event => {
|
|
||||||
if (
|
|
||||||
!this.props.disableMenuButtonBehavior &&
|
|
||||||
this.props.displayMenu &&
|
|
||||||
this.menuButtonRef.current &&
|
|
||||||
!this.menuButtonRef.current.contains(event.target)
|
|
||||||
) {
|
|
||||||
this.props.toggleDisplayMenu();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMediaChange = matches => {
|
|
||||||
if (!matches && this.props.displayMenu) {
|
|
||||||
this.props.toggleDisplayMenu();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {
|
|
||||||
disableMenuButtonBehavior,
|
|
||||||
disableSettings,
|
|
||||||
displayMenu,
|
|
||||||
mediaBreakpoint,
|
|
||||||
toggleDisplayMenu
|
|
||||||
} = this.props;
|
|
||||||
return (
|
return (
|
||||||
<header>
|
<header>
|
||||||
<nav id='top-nav'>
|
<nav id='top-nav'>
|
||||||
@ -81,65 +22,16 @@ class Header extends Component {
|
|||||||
<NavLogo />
|
<NavLogo />
|
||||||
</Link>
|
</Link>
|
||||||
{disableSettings ? null : <FCCSearch />}
|
{disableSettings ? null : <FCCSearch />}
|
||||||
<Media maxWidth={mediaBreakpoint} onChange={this.handleMediaChange}>
|
{navigationMenu ? (
|
||||||
{matches => [
|
navigationMenu
|
||||||
matches && (
|
) : (
|
||||||
<button
|
<NavigationMenu disableSettings={disableSettings} />
|
||||||
aria-expanded={displayMenu}
|
)}
|
||||||
className={
|
|
||||||
'menu-button' + (displayMenu ? ' menu-button-open' : '')
|
|
||||||
}
|
|
||||||
key='menu-button'
|
|
||||||
onClick={toggleDisplayMenu}
|
|
||||||
ref={this.menuButtonRef}
|
|
||||||
>
|
|
||||||
Menu
|
|
||||||
</button>
|
|
||||||
),
|
|
||||||
(!matches || (displayMenu && !disableMenuButtonBehavior)) && (
|
|
||||||
<ul id='top-right-nav' key='top-right-nav'>
|
|
||||||
<li>
|
|
||||||
<Link className='top-right-nav-link' to='/learn'>
|
|
||||||
Learn
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className='top-right-nav-link'
|
|
||||||
external={true}
|
|
||||||
to='/forum'
|
|
||||||
>
|
|
||||||
Forum
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className='top-right-nav-link'
|
|
||||||
external={true}
|
|
||||||
to='/news'
|
|
||||||
>
|
|
||||||
News
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<UserState disableSettings={disableSettings} />
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
)
|
|
||||||
]}
|
|
||||||
</Media>
|
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Header.propTypes = propTypes;
|
Header.propTypes = propTypes;
|
||||||
Header.defaultProps = {
|
|
||||||
mediaBreakpoint: '734px'
|
|
||||||
};
|
|
||||||
|
|
||||||
export default connect(
|
export default Header;
|
||||||
mapStateToProps,
|
|
||||||
mapDispatchToProps
|
|
||||||
)(Header);
|
|
||||||
|
@ -56,8 +56,6 @@ const metaKeywords = [
|
|||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
children: PropTypes.node.isRequired,
|
children: PropTypes.node.isRequired,
|
||||||
disableMenuButtonBehavior: PropTypes.bool,
|
|
||||||
disableSettings: PropTypes.bool,
|
|
||||||
fetchUser: PropTypes.func.isRequired,
|
fetchUser: PropTypes.func.isRequired,
|
||||||
flashMessages: PropTypes.arrayOf(
|
flashMessages: PropTypes.arrayOf(
|
||||||
PropTypes.shape({
|
PropTypes.shape({
|
||||||
@ -70,7 +68,7 @@ const propTypes = {
|
|||||||
isOnline: PropTypes.bool.isRequired,
|
isOnline: PropTypes.bool.isRequired,
|
||||||
isSignedIn: PropTypes.bool,
|
isSignedIn: PropTypes.bool,
|
||||||
landingPage: PropTypes.bool,
|
landingPage: PropTypes.bool,
|
||||||
mediaBreakpoint: PropTypes.string,
|
navigationMenu: PropTypes.element.isRequired,
|
||||||
onlineStatusChange: PropTypes.func.isRequired,
|
onlineStatusChange: PropTypes.func.isRequired,
|
||||||
removeFlashMessage: PropTypes.func.isRequired,
|
removeFlashMessage: PropTypes.func.isRequired,
|
||||||
showFooter: PropTypes.bool
|
showFooter: PropTypes.bool
|
||||||
@ -136,14 +134,12 @@ class DefaultLayout extends Component {
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
disableSettings,
|
|
||||||
hasMessages,
|
hasMessages,
|
||||||
flashMessages = [],
|
flashMessages = [],
|
||||||
removeFlashMessage,
|
removeFlashMessage,
|
||||||
landingPage,
|
landingPage,
|
||||||
showFooter = true,
|
showFooter = true,
|
||||||
mediaBreakpoint,
|
navigationMenu,
|
||||||
disableMenuButtonBehavior,
|
|
||||||
isOnline,
|
isOnline,
|
||||||
isSignedIn
|
isSignedIn
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -162,11 +158,7 @@ class DefaultLayout extends Component {
|
|||||||
>
|
>
|
||||||
<style>{fontawesome.dom.css()}</style>
|
<style>{fontawesome.dom.css()}</style>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<Header
|
<Header disableSettings={landingPage} navigationMenu={navigationMenu} />
|
||||||
disableMenuButtonBehavior={disableMenuButtonBehavior}
|
|
||||||
disableSettings={disableSettings}
|
|
||||||
mediaBreakpoint={mediaBreakpoint}
|
|
||||||
/>
|
|
||||||
<div className={`default-layout ${landingPage ? 'landing-page' : ''}`}>
|
<div className={`default-layout ${landingPage ? 'landing-page' : ''}`}>
|
||||||
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
<OfflineWarning isOnline={isOnline} isSignedIn={isSignedIn} />
|
||||||
{hasMessages ? (
|
{hasMessages ? (
|
||||||
|
45
client/src/components/layouts/components/guide/NavMenu.js
Normal file
45
client/src/components/layouts/components/guide/NavMenu.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { bindActionCreators } from 'redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
|
||||||
|
import MenuButton from '../../../../components/Header/components/MenuButton';
|
||||||
|
import MenuLinks from '../../../../components/Header/components/MenuLinks';
|
||||||
|
|
||||||
|
import { toggleDisplayMenu, displayMenuSelector } from './redux';
|
||||||
|
|
||||||
|
const mapStateToProps = createSelector(
|
||||||
|
displayMenuSelector,
|
||||||
|
displayMenu => ({
|
||||||
|
displayMenu
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch =>
|
||||||
|
bindActionCreators({ toggleDisplayMenu }, dispatch);
|
||||||
|
|
||||||
|
function GuideNavigationMenu(props) {
|
||||||
|
const { displayMenu, toggleDisplayMenu } = props;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MenuButton
|
||||||
|
className={'guide-menu-button'}
|
||||||
|
displayMenu={displayMenu}
|
||||||
|
onClick={toggleDisplayMenu}
|
||||||
|
/>
|
||||||
|
<MenuLinks className={'guide-top-nav'} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
GuideNavigationMenu.displayName = 'GuideNavigationMenu';
|
||||||
|
GuideNavigationMenu.propTypes = {
|
||||||
|
displayMenu: PropTypes.bool.isRequired,
|
||||||
|
toggleDisplayMenu: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(GuideNavigationMenu);
|
@ -124,6 +124,15 @@
|
|||||||
.content {
|
.content {
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
#top-right-nav.guide-top-nav {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 993px) {
|
||||||
|
#top-nav .guide-menu-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content,
|
.content,
|
||||||
|
Reference in New Issue
Block a user