feat(nav): remove sidecar

This commit is contained in:
Berkeley Martinez
2016-12-29 22:04:05 -08:00
parent 31960fee90
commit 1892654dab
24 changed files with 98 additions and 909 deletions
-33
View File
@@ -3,13 +3,10 @@ import { Button, Row } from 'react-bootstrap';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import MapDrawer from './components/Map-Drawer.jsx';
import {
fetchUser,
initWindowHeight,
updateNavHeight,
toggleMapDrawer,
toggleMainChat,
updateAppLang,
trackEvent,
loadCurrentChallenge
@@ -26,8 +23,6 @@ const mapDispatchToProps = {
updateNavHeight,
fetchUser,
submitChallenge,
toggleMapDrawer,
toggleMainChat,
updateAppLang,
trackEvent,
loadCurrentChallenge
@@ -37,23 +32,17 @@ const mapStateToProps = createSelector(
userSelector,
state => state.app.isSignInAttempted,
state => state.app.toast,
state => state.app.isMapDrawerOpen,
state => state.app.isMapAlreadyLoaded,
state => state.challengesApp.toast,
(
{ user: { username, points, picture } },
isSignInAttempted,
toast,
isMapDrawerOpen,
isMapAlreadyLoaded,
) => ({
username,
points,
picture,
toast,
showLoading: !isSignInAttempted,
isMapDrawerOpen,
isMapAlreadyLoaded,
isSignedIn: !!username
})
);
@@ -68,10 +57,6 @@ const propTypes = {
updateNavHeight: PropTypes.func,
initWindowHeight: PropTypes.func,
submitChallenge: PropTypes.func,
isMapDrawerOpen: PropTypes.bool,
isMapAlreadyLoaded: PropTypes.bool,
toggleMapDrawer: PropTypes.func,
toggleMainChat: PropTypes.func,
fetchUser: PropTypes.func,
showLoading: PropTypes.bool,
params: PropTypes.object,
@@ -79,7 +64,6 @@ const propTypes = {
trackEvent: PropTypes.func.isRequired,
loadCurrentChallenge: PropTypes.func.isRequired
};
const contextTypes = { router: PropTypes.object };
// export plain class for testing
export class FreeCodeCamp extends React.Component {
@@ -112,30 +96,19 @@ export class FreeCodeCamp extends React.Component {
}
render() {
const { router } = this.context;
const {
username,
points,
picture,
updateNavHeight,
isMapDrawerOpen,
isMapAlreadyLoaded,
toggleMapDrawer,
toggleMainChat,
showLoading,
params: { lang },
trackEvent,
loadCurrentChallenge
} = this.props;
const navProps = {
isOnMap: router.isActive(`/${lang}/map`),
username,
points,
picture,
updateNavHeight,
toggleMapDrawer,
toggleMainChat,
showLoading,
trackEvent,
loadCurrentChallenge
};
@@ -146,11 +119,6 @@ export class FreeCodeCamp extends React.Component {
<Row>
{ this.props.children }
</Row>
<MapDrawer
isAlreadyLoaded={ isMapAlreadyLoaded }
isOpen={ isMapDrawerOpen }
toggleMapDrawer={ toggleMapDrawer }
/>
<Toasts />
</div>
);
@@ -158,7 +126,6 @@ export class FreeCodeCamp extends React.Component {
}
FreeCodeCamp.displayName = 'FreeCodeCamp';
FreeCodeCamp.contextTypes = contextTypes;
FreeCodeCamp.propTypes = propTypes;
export default connect(
-49
View File
@@ -1,49 +0,0 @@
import React, { PropTypes } from 'react';
import classnames from 'classnames';
export default class Drawer extends React.Component {
static displayName = 'Drawer';
static propTypes = {
children: PropTypes.node,
isOpen: PropTypes.bool,
closeDrawer: PropTypes.func,
closeAria: PropTypes.string,
newTabLink: PropTypes.string,
newTabAria: PropTypes.string
};
render() {
const {
isOpen,
closeDrawer,
closeAria,
children,
newTabAria,
newTabLink
} = this.props;
const drawerClass = classnames({
drawer: true,
'is-collapsed': !isOpen
});
return (
<aside className={ drawerClass }>
<div className='drawer-action-bar'>
<a
aria-label={ newTabAria }
className='drawer-action-item drawer-action-pop-out'
href={ newTabLink }
target='_blank'
/>
<button
aria-label={ closeAria }
className='drawer-action-item drawer-action-collapse'
onClick={ closeDrawer }
/>
</div>
<div className='drawer-content'>
{ children }
</div>
</aside>
);
}
}
-27
View File
@@ -1,27 +0,0 @@
import React, { PropTypes } from 'react';
import { Alert } from 'react-bootstrap';
export default React.createClass({
displayName: 'FlashQueue',
propTypes: {
messages: PropTypes.array
},
renderMessages(messages) {
return messages.map(() => {
return (
<Alert />
);
});
},
render() {
const { messages = [] } = this.props;
return (
<div>
{ this.renderMessages(messages) }
</div>
);
}
});
-33
View File
@@ -1,33 +0,0 @@
import React, { PropTypes } from 'react';
import NoSSR from 'react-no-ssr';
import Drawer from './Drawer.jsx';
import ShowMap from '../routes/challenges/components/map/Map.jsx';
export default class MapDrawer extends React.Component {
static displayName = 'MapDrawer';
static propTypes = {
isOpen: PropTypes.bool,
isAlreadyLoaded: PropTypes.bool,
toggleMapDrawer: PropTypes.func
};
render() {
const { isOpen, isAlreadyLoaded, toggleMapDrawer } = this.props;
return (
<Drawer
closeAria='close map aside'
closeDrawer={ toggleMapDrawer }
isOpen={ isOpen }
newTabAria='open map in new tab'
newTabLink='/map'
>
<NoSSR>
<div>
{ isAlreadyLoaded || isOpen ? <ShowMap /> : null }
</div>
</NoSSR>
</Drawer>
);
}
}
-62
View File
@@ -11,7 +11,6 @@ import {
import navLinks from './links.json';
import AvatarPointsNavItem from './Avatar-Points-Nav-Item.jsx';
import NoPropsPassthrough from '../../utils/No-Props-Passthrough.jsx';
const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg';
@@ -34,10 +33,7 @@ const propTypes = {
picture: PropTypes.string,
signedIn: PropTypes.bool,
username: PropTypes.string,
isOnMap: PropTypes.bool,
updateNavHeight: PropTypes.func,
toggleMapDrawer: PropTypes.func,
toggleMainChat: PropTypes.func,
showLoading: PropTypes.bool,
trackEvent: PropTypes.func.isRequired,
loadCurrentChallenge: PropTypes.func.isRequired
@@ -80,59 +76,6 @@ export default class FCCNav extends React.Component {
this.props.loadCurrentChallenge();
}
renderMapLink(isOnMap, toggleMapDrawer) {
if (isOnMap) {
return (
<NoPropsPassthrough>
<li role='presentation'>
<a
href='#'
onClick={ this.handleMapClickOnMap }
>
Map
</a>
</li>
</NoPropsPassthrough>
);
}
return (
<LinkContainer
eventKey={ 1 }
to='/map'
>
<NavItem
onClick={ e => {
if (!(e.ctrlKey || e.metaKey)) {
e.preventDefault();
toggleMapDrawer();
}
}}
target='/map'
>
Map
</NavItem>
</LinkContainer>
);
}
renderChat(toggleMainChat) {
return (
<NavItem
eventKey={ 2 }
href='//gitter.im/freecodecamp/freecodecamp'
onClick={ e => {
if (!(e.ctrlKey || e.metaKey)) {
e.preventDefault();
toggleMainChat();
}
}}
target='_blank'
>
Chat
</NavItem>
);
}
renderLinks() {
return navLinks.map(({ content, link, react, target }, index) => {
if (react) {
@@ -195,9 +138,6 @@ export default class FCCNav extends React.Component {
username,
points,
picture,
isOnMap,
toggleMapDrawer,
toggleMainChat,
showLoading
} = this.props;
@@ -227,8 +167,6 @@ export default class FCCNav extends React.Component {
navbar={ true }
pullRight={ true }
>
{ this.renderMapLink(isOnMap, toggleMapDrawer) }
{ this.renderChat(toggleMainChat) }
{ this.renderLinks() }
{ this.renderSignIn(username, points, picture, showLoading) }
</Nav>
+9 -1
View File
@@ -1,6 +1,14 @@
[{
"content": "Map",
"link": "/map",
"react": true
},{
"content": "Chat",
"link": "https://gitter.im/freecodecamp/home",
"target": "_blank"
},{
"content": "Forum",
"link": "http://forum.freecodecamp.com/",
"link": "https://forum.freecodecamp.com/",
"target": "_blank"
},{
"content": "About",
-57
View File
@@ -136,63 +136,6 @@ export const doActionOnError = actionCreator => error => Observable.of(
actionCreator()
);
// drawers
export const toggleMapDrawer = createAction(
types.toggleMapDrawer,
null,
() => createEventMeta({
category: 'Nav',
action: 'toggled',
label: 'Map drawer toggled'
})
);
export const closeMapDrawer = createAction(
types.closeMapDrawer,
null,
() => createEventMeta({
category: 'Nav',
action: 'clicked',
label: 'Map drawer closed'
})
);
export const toggleMainChat = createAction(
types.toggleMainChat,
null,
() => createEventMeta({
category: 'Nav',
action: 'toggled',
label: 'Main chat toggled'
})
);
export const toggleHelpChat = createAction(
types.toggleHelpChat,
null,
() => createEventMeta({
category: 'Challenge',
action: 'toggled',
label: 'help chat toggled'
})
);
export const openHelpChat = createAction(
types.openHelpChat,
null,
() => createEventMeta({
category: 'Challenge',
action: 'opened',
label: 'help chat opened'
})
);
export const closeHelpChat = createAction(
types.closeHelpChat,
null,
() => createEventMeta({
category: 'Challenge',
action: 'closed',
label: 'help chat closed'
})
);
export const toggleNightMode = createAction(
types.toggleNightMode,
// we use this function to avoid hanging onto the eventObject
-27
View File
@@ -9,8 +9,6 @@ const initialState = {
csrfToken: '',
windowHeight: 0,
navHeight: 0,
isMainChatOpen: false,
isHelpChatOpen: false,
theme: 'default'
};
@@ -51,31 +49,6 @@ export default handleActions(
...state,
navHeight
}),
[types.toggleMapDrawer]: state => ({
...state,
isMapAlreadyLoaded: true,
isMapDrawerOpen: !state.isMapDrawerOpen
}),
[types.closeMapDrawer]: state => ({
...state,
isMapDrawerOpen: false
}),
[types.toggleMainChat]: state => ({
...state,
isMainChatOpen: !state.isMainChatOpen
}),
[types.toggleHelpChat]: state => ({
...state,
isHelpChatOpen: !state.isHelpChatOpen
}),
[types.openHelpChat]: state => ({
...state,
isHelpChatOpen: true
}),
[types.closeHelpChat]: state => ({
...state,
isHelpChatOpen: false
}),
[types.delayedRedirect]: (state, { payload }) => ({
...state,
delayedRedirect: payload
-14
View File
@@ -30,20 +30,6 @@ export default createTypes([
'updateChallengesData',
'updateHikesData',
// drawers
'toggleMapDrawer',
'closeMapDrawer',
'toggleWikiDrawer',
// chat
'openMainChat',
'closeMainChat',
'toggleMainChat',
'openHelpChat',
'closeHelpChat',
'toggleHelpChat',
// night mode
'toggleNightMode',
'updateTheme',
@@ -16,13 +16,11 @@ import {
unlockUntrustedCode
} from '../../redux/actions';
import { makeToast } from '../../../../toasts/redux/actions';
import { toggleHelpChat } from '../../../../redux/actions';
const bindableActions = {
const mapDispatchToProps = {
makeToast,
executeChallenge,
updateHint,
toggleHelpChat,
openBugModal,
unlockUntrustedCode
};
@@ -34,6 +32,7 @@ const mapStateToProps = createSelector(
state => state.challengesApp.output,
state => state.challengesApp.hintIndex,
state => state.challengesApp.isCodeLocked,
state => state.challengesApp.helpChatRoom,
(
{
challenge: {
@@ -47,7 +46,8 @@ const mapStateToProps = createSelector(
tests,
output,
hintIndex,
isCodeLocked
isCodeLocked,
helpChatRoom
) => ({
title,
description,
@@ -55,7 +55,8 @@ const mapStateToProps = createSelector(
tests,
output,
hint: hints[hintIndex],
isCodeLocked
isCodeLocked,
helpChatRoom
})
);
@@ -69,17 +70,18 @@ export class SidePanel extends PureComponent {
static propTypes = {
description: PropTypes.arrayOf(PropTypes.string),
height: PropTypes.number,
helpChatRoom: PropTypes.string,
hint: PropTypes.string,
isCodeLocked: PropTypes.bool,
output: PropTypes.string,
tests: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string,
output: PropTypes.string,
hint: PropTypes.string,
updateHint: PropTypes.func,
makeToast: PropTypes.func,
toggleHelpChat: PropTypes.func,
executeChallenge: PropTypes.func,
openBugModal: PropTypes.func,
makeToast: PropTypes.func,
unlockUntrustedCode: PropTypes.func,
isCodeLocked: PropTypes.bool,
executeChallenge: PropTypes.func
updateHint: PropTypes.func
};
renderDescription(description = [ 'Happy Coding!' ], descriptionRegex) {
@@ -119,7 +121,7 @@ export class SidePanel extends PureComponent {
executeChallenge,
updateHint,
makeToast,
toggleHelpChat,
helpChatRoom,
openBugModal,
isCodeLocked,
unlockUntrustedCode
@@ -150,11 +152,11 @@ export class SidePanel extends PureComponent {
</div>
<ToolPanel
executeChallenge={ executeChallenge }
helpChatRoom={ helpChatRoom }
hint={ hint }
isCodeLocked={ isCodeLocked }
makeToast={ makeToast }
openBugModal={ openBugModal }
toggleHelpChat={ toggleHelpChat }
unlockUntrustedCode={ unlockUntrustedCode }
updateHint={ updateHint }
/>
@@ -168,5 +170,5 @@ export class SidePanel extends PureComponent {
export default connect(
mapStateToProps,
bindableActions
mapDispatchToProps
)(SidePanel);
@@ -9,25 +9,24 @@ const unlockWarning = (
</h4>
</Tooltip>
);
const propTypes = {
executeChallenge: PropTypes.func.isRequired,
helpChatRoom: PropTypes.string,
hint: PropTypes.string,
isCodeLocked: PropTypes.bool,
makeToast: PropTypes.func.isRequired,
openBugModal: PropTypes.func.isRequired,
unlockUntrustedCode: PropTypes.func.isRequired,
updateHint: PropTypes.func.isRequired
};
export default class ToolPanel extends PureComponent {
constructor(...props) {
super(...props);
this.makeHint = this.makeHint.bind(this);
this.makeReset = this.makeReset.bind(this);
}
static displayName = 'ToolPanel';
static propTypes = {
executeChallenge: PropTypes.func.isRequired,
updateHint: PropTypes.func.isRequired,
hint: PropTypes.string,
isCodeLocked: PropTypes.bool,
unlockUntrustedCode: PropTypes.func.isRequired,
toggleHelpChat: PropTypes.func.isRequired,
openBugModal: PropTypes.func.isRequired,
makeToast: PropTypes.func.isRequired
};
makeHint() {
this.props.makeToast({
message: this.props.hint,
@@ -93,10 +92,10 @@ export default class ToolPanel extends PureComponent {
render() {
const {
executeChallenge,
helpChatRoom,
hint,
isCodeLocked,
executeChallenge,
toggleHelpChat,
openBugModal,
unlockUntrustedCode
} = this.props;
@@ -126,8 +125,9 @@ export default class ToolPanel extends PureComponent {
<Button
bsSize='large'
bsStyle='primary'
componentClass='label'
onClick={ toggleHelpChat }
componentClass='a'
href={ `https://gitter.im/freecodecamp/${helpChatRoom}` }
target='_blank'
>
Help
</Button>
@@ -145,3 +145,6 @@ export default class ToolPanel extends PureComponent {
);
}
}
ToolPanel.displayName = 'ToolPanel';
ToolPanel.propTypes = propTypes;
@@ -9,9 +9,21 @@ import debug from 'debug';
import { updateCurrentChallenge } from '../../redux/actions';
import { makePanelHiddenSelector } from '../../redux/selectors';
import { userSelector } from '../../../../redux/selectors';
import { closeMapDrawer } from '../../../../redux/actions';
const bindableActions = { closeMapDrawer, updateCurrentChallenge };
const propTypes = {
block: PropTypes.string,
challenge: PropTypes.object,
dashedName: PropTypes.string,
isComingSoon: PropTypes.bool,
isCompleted: PropTypes.bool,
isDev: PropTypes.bool,
isHidden: PropTypes.bool,
isLocked: PropTypes.bool,
isRequired: PropTypes.bool,
title: PropTypes.string,
updateCurrentChallenge: PropTypes.func.isRequired
};
const mapDispatchToProps = { updateCurrentChallenge };
const makeMapStateToProps = () => createSelector(
userSelector,
(_, props) => props.dashedName,
@@ -48,24 +60,8 @@ export class Challenge extends PureComponent {
super(...args);
this.handleChallengeClick = this.handleChallengeClick.bind(this);
}
static displayName = 'Challenge';
static propTypes = {
title: PropTypes.string,
dashedName: PropTypes.string,
block: PropTypes.string,
isLocked: PropTypes.bool,
isRequired: PropTypes.bool,
isComingSoon: PropTypes.bool,
isCompleted: PropTypes.bool,
isDev: PropTypes.bool,
isHidden: PropTypes.bool,
challenge: PropTypes.object,
updateCurrentChallenge: PropTypes.func.isRequired,
closeMapDrawer: PropTypes.func.isRequired
};
handleChallengeClick() {
this.props.closeMapDrawer();
this.props.updateCurrentChallenge(this.props.challenge);
}
@@ -161,4 +157,10 @@ export class Challenge extends PureComponent {
}
}
export default connect(makeMapStateToProps, bindableActions)(Challenge);
Challenge.propTypes = propTypes;
Challenge.dispalyName = 'Challenge';
export default connect(
makeMapStateToProps,
mapDispatchToProps
)(Challenge);
@@ -15,40 +15,40 @@ import {
simpleProject,
frontEndProject
} from '../../../../utils/challengeTypes';
import { toggleHelpChat } from '../../../../redux/actions';
const bindableActions = {
const propTypes = {
isSignedIn: PropTypes.bool,
isSimple: PropTypes.bool,
isFrontEnd: PropTypes.bool,
isSubmitting: PropTypes.bool,
helpChatRoom: PropTypes.string.isRequired,
openBugModal: PropTypes.func.isRequired,
submitChallenge: PropTypes.func.isRequired
};
const mapDispatchToProps = {
submitChallenge,
toggleHelpChat,
openBugModal
};
const mapStateToProps = createSelector(
challengeSelector,
state => state.app.isSignedIn,
state => state.challengesApp.isSubmitting,
state => state.challengesApp.helpChatRoom,
(
{ challenge: { challengeType = simpleProject } },
isSignedIn,
isSubmitting
isSubmitting,
helpChatRoom,
) => ({
isSignedIn,
isSubmitting,
helpChatRoom,
isSimple: challengeType === simpleProject,
isFrontEnd: challengeType === frontEndProject
})
);
export class ToolPanel extends PureComponent {
static propTypes = {
isSignedIn: PropTypes.bool,
isSimple: PropTypes.bool,
isFrontEnd: PropTypes.bool,
isSubmitting: PropTypes.bool,
toggleHelpChat: PropTypes.func.isRequired,
openBugModal: PropTypes.func.isRequired,
submitChallenge: PropTypes.func.isRequired
};
renderSubmitButton(isSignedIn, submitChallenge) {
const buttonCopy = isSignedIn ?
'Submit and go to my next challenge' :
@@ -71,8 +71,8 @@ export class ToolPanel extends PureComponent {
isSimple,
isSignedIn,
isSubmitting,
helpChatRoom,
submitChallenge,
toggleHelpChat,
openBugModal
} = this.props;
@@ -89,8 +89,9 @@ export class ToolPanel extends PureComponent {
<Button
bsStyle='primary'
className='btn-primary-ghost btn-big'
componentClass='div'
onClick={ toggleHelpChat }
componentClass='a'
href={ `https://gitter.im/freecodecamp/${helpChatRoom}` }
target='_blank'
>
Help
</Button>
@@ -108,7 +109,10 @@ export class ToolPanel extends PureComponent {
}
}
ToolPanel.propTypes = propTypes;
ToolPanel.displayName = 'ToolPanel';
export default connect(
mapStateToProps,
bindableActions
mapDispatchToProps
)(ToolPanel);