diff --git a/common/app/Nav/Nav.jsx b/common/app/Nav/Nav.jsx index 463519a77e..2fb225beca 100644 --- a/common/app/Nav/Nav.jsx +++ b/common/app/Nav/Nav.jsx @@ -2,53 +2,22 @@ import React from 'react'; import PropTypes from 'prop-types'; import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import capitalize from 'lodash/capitalize'; import { createSelector } from 'reselect'; import FCCSearchBar from 'react-freecodecamp-search'; import { - MenuItem, - Nav, - NavDropdown, - NavItem, - Navbar, - NavbarBrand + Navbar } from 'react-bootstrap'; -import NoPropsPassThrough from '../utils/No-Props-Passthrough.jsx'; -import { Link } from '../Router'; -import navLinks from './links.json'; -import SignUp from './Sign-Up.jsx'; -import BinButton from './Bin-Button.jsx'; +import { BinButtons, NavLogo, NavLinks } from './components'; import { clickOnLogo, - clickOnMap, - openDropdown, - closeDropdown, - createNavLinkActionCreator, - - dropdownSelector + clickOnMap } from './redux'; -import { isSignedInSelector, signInLoadingSelector } from '../redux'; import { panesSelector } from '../Panes/redux'; -import { onRouteCurrentChallenge } from '../routes/Challenges/redux'; - - -const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg'; -// TODO @freecodecamp-team: place this glyph in S3 like above, PR in /assets -const fCCglyph = 'https://raw.githubusercontent.com/freeCodeCamp/assets/' + - '3b9cafc312802199ebba8b31fb1ed9b466a3efbb/assets/logos/FFCFire.png'; const mapStateToProps = createSelector( - isSignedInSelector, - dropdownSelector, - signInLoadingSelector, panesSelector, - ( - isSignedIn, - isDropdownOpen, - showLoading, - panes, - ) => { + panes => { return { panes: panes.map(({ name, type }) => { return { @@ -56,20 +25,13 @@ const mapStateToProps = createSelector( action: type }; }, {}), - isDropdownOpen, - isSignedIn, - showLoading + shouldShowMapButton: panes.length === 0 }; } ); function mapDispatchToProps(dispatch) { - const dispatchers = bindActionCreators(navLinks.reduce( - (mdtp, { content }) => { - const handler = `handle${capitalize(content)}Click`; - mdtp[handler] = createNavLinkActionCreator(content); - return mdtp; - }, + const dispatchers = bindActionCreators( { clickOnMap: e => { e.preventDefault(); @@ -78,11 +40,10 @@ function mapDispatchToProps(dispatch) { clickOnLogo: e => { e.preventDefault(); return clickOnLogo(); - }, - closeDropdown: () => closeDropdown(), - openDropdown: () => openDropdown() - } - ), dispatch); + } + }, + dispatch + ); dispatchers.dispatch = dispatch; return () => dispatchers; } @@ -105,153 +66,42 @@ function mergeProps(stateProps, dispatchProps, ownProps) { const propTypes = { clickOnLogo: PropTypes.func.isRequired, clickOnMap: PropTypes.func.isRequired, - closeDropdown: PropTypes.func.isRequired, - isDropdownOpen: PropTypes.bool, - isSignedIn: PropTypes.bool, - openDropdown: PropTypes.func.isRequired, panes: PropTypes.array, - showLoading: PropTypes.bool, - signedIn: PropTypes.bool + shouldShowMapButton: PropTypes.bool }; -export class FCCNav extends React.Component { - renderLink(isNavItem, { isReact, isDropdown, content, link, links, target }) { - const Component = isNavItem ? NavItem : MenuItem; - const { - isDropdownOpen, - openDropdown, - closeDropdown - } = this.props; - - if (isDropdown) { - // adding a noop to NavDropdown to disable false warning - // about controlled component - return ( - - { links.map(this.renderLink.bind(this, false)) } - - ); - } - if (isReact) { - return ( - - - { content } - - - ); - } - return ( - - { content } - - ); - } - - render() { - const { - panes, - isSignedIn, - clickOnLogo, - clickOnMap, - showLoading - } = this.props; - - const shouldShowMapButton = panes.length === 0; - return ( - -
- - - - - learn to code javascript at freeCodeCamp logo - learn to code javascript at freeCodeCamp logo - - - - - - - -
-
- ); - } +function FCCNav(props) { + const { + panes, + clickOnLogo, + clickOnMap, + shouldShowMapButton + } = props; + return ( + +
+ + + + + + + + + +
+
+ ); } FCCNav.displayName = 'FCCNav'; diff --git a/common/app/Nav/Bin-Button.jsx b/common/app/Nav/components/Bin-Button.jsx similarity index 100% rename from common/app/Nav/Bin-Button.jsx rename to common/app/Nav/components/Bin-Button.jsx diff --git a/common/app/Nav/components/BinButtons.jsx b/common/app/Nav/components/BinButtons.jsx new file mode 100644 index 0000000000..67ae2072d6 --- /dev/null +++ b/common/app/Nav/components/BinButtons.jsx @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Nav } from 'react-bootstrap'; +import BinButton from './Bin-Button.jsx'; + +const propTypes = { + panes: PropTypes.arrayOf( + PropTypes.shape({ + actionCreator: PropTypes.func.isRequired, + content: PropTypes.string.isRequired + }) + ) +}; + +function BinButtons({ panes }) { + return ( +
+ +
+ ); +} + +BinButtons.displayName = 'BinButtons'; +BinButtons.propTypes = propTypes; + +export default BinButtons; diff --git a/common/app/Nav/components/NavLinks.jsx b/common/app/Nav/components/NavLinks.jsx new file mode 100644 index 0000000000..2a8cec2024 --- /dev/null +++ b/common/app/Nav/components/NavLinks.jsx @@ -0,0 +1,167 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { capitalize } from 'lodash'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; +import { MenuItem, NavDropdown, NavItem, Nav } from 'react-bootstrap'; + +import navLinks from '../links.json'; +import SignUp from './Sign-Up.jsx'; +import NoPropsPassThrough from '../../utils/No-Props-Passthrough.jsx'; +import { Link } from '../../Router'; + +import { onRouteCurrentChallenge } from '../../routes/Challenges/redux'; +import { + openDropdown, + closeDropdown, + dropdownSelector, + createNavLinkActionCreator +} from '../redux'; +import { isSignedInSelector, signInLoadingSelector } from '../../redux'; + +const mapStateToProps = createSelector( + isSignedInSelector, + dropdownSelector, + signInLoadingSelector, + (isSignedIn, isDropdownOpen, showLoading) => ({ + isDropdownOpen, + isSignedIn, + navLinks, + showLoading + }) +); + +function mapDispatchToProps(dispatch) { + return bindActionCreators( + { + ...navLinks.reduce( + (mdtp, { content }) => { + const handler = `handle${capitalize(content)}Click`; + mdtp[handler] = createNavLinkActionCreator(content); + return mdtp; + }), + closeDropdown, + openDropdown + }, + dispatch + ); +} + +const navLinkPropType = PropTypes.shape({ + content: PropTypes.string, + link: PropTypes.string, + isDropdown: PropTypes.bool, + target: PropTypes.string, + links: PropTypes.array +}); + +const propTypes = { + clickOnMap: PropTypes.func.isRequired, + closeDropdown: PropTypes.func.isRequired, + isDropdownOpen: PropTypes.bool, + isSignedIn: PropTypes.bool, + navLinks: PropTypes.arrayOf(navLinkPropType), + openDropdown: PropTypes.func.isRequired, + shouldShowMapButton: PropTypes.bool, + showLoading: PropTypes.bool +}; + +class NavLinks extends PureComponent { + + renderLink(isNavItem, { isReact, isDropdown, content, link, links, target }) { + const Component = isNavItem ? NavItem : MenuItem; + const { + isDropdownOpen, + openDropdown, + closeDropdown + } = this.props; + + if (isDropdown) { + // adding a noop to NavDropdown to disable false warning + // about controlled component + return ( + + { links.map(this.renderLink.bind(this, false)) } + + ); + } + if (isReact) { + return ( + + + { content } + + + ); + } + return ( + + { content } + + ); + } + + render() { + const { + shouldShowMapButton, + clickOnMap, + showLoading, + isSignedIn, + navLinks + } = this.props; + return ( + + ); + } +} + +NavLinks.displayName = 'NavLinks'; +NavLinks.propTypes = propTypes; + +export default connect(mapStateToProps, mapDispatchToProps)(NavLinks); diff --git a/common/app/Nav/components/NavLogo.jsx b/common/app/Nav/components/NavLogo.jsx new file mode 100644 index 0000000000..9f06918d61 --- /dev/null +++ b/common/app/Nav/components/NavLogo.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { NavbarBrand } from 'react-bootstrap'; + +const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg'; +// TODO @freecodecamp-team: place this glyph in S3 like above, PR in /assets +const fCCglyph = 'https://raw.githubusercontent.com/freeCodeCamp/assets/' + + '3b9cafc312802199ebba8b31fb1ed9b466a3efbb/assets/logos/FFCFire.png'; + +const propTypes = { + clickOnLogo: PropTypes.func.isRequired +}; + +function NavLogo({ clickOnLogo }) { + return ( + + + learn to code javascript at freeCodeCamp logo + learn to code javascript at freeCodeCamp logo + + + ); +} + +NavLogo.displayName = 'NavLogo'; +NavLogo.propTypes = propTypes; + +export default NavLogo; diff --git a/common/app/Nav/Sign-Up.jsx b/common/app/Nav/components/Sign-Up.jsx similarity index 87% rename from common/app/Nav/Sign-Up.jsx rename to common/app/Nav/components/Sign-Up.jsx index b91c9e895a..922ba52eca 100644 --- a/common/app/Nav/Sign-Up.jsx +++ b/common/app/Nav/components/Sign-Up.jsx @@ -2,8 +2,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { NavItem } from 'react-bootstrap'; -import { Link } from '../Router'; -import { onRouteSettings } from '../routes/Settings/redux'; +import { Link } from '../../Router'; +import { onRouteSettings } from '../../routes/Settings/redux'; const propTypes = { showLoading: PropTypes.bool, diff --git a/common/app/Nav/components/index.js b/common/app/Nav/components/index.js new file mode 100644 index 0000000000..a406b1ec16 --- /dev/null +++ b/common/app/Nav/components/index.js @@ -0,0 +1,3 @@ +export { default as BinButtons } from './BinButtons.jsx'; +export { default as NavLogo } from './NavLogo.jsx'; +export { default as NavLinks } from './NavLinks.jsx';