feat(tool-panel): Make buttons look and act like current productiion (#16580)

This commit is contained in:
Stuart Taylor
2018-01-29 17:26:03 +00:00
committed by Quincy Larson
parent 4ce84166ed
commit e3a522aa5e
9 changed files with 68 additions and 276 deletions

View File

@@ -1,95 +0,0 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Button, Modal } from 'react-bootstrap';
import ns from './ns.json';
import {
createIssue,
openIssueSearch,
closeBugModal,
bugModalSelector
} from './redux';
const mapStateToProps = state => ({ isOpen: bugModalSelector(state) });
const mapDispatchToProps = { createIssue, openIssueSearch, closeBugModal };
const bugLink = 'http://forum.freecodecamp.org/t/how-to-report-a-bug/19543';
const propTypes = {
closeBugModal: PropTypes.func,
createIssue: PropTypes.func,
isOpen: PropTypes.bool,
openIssueSearch: PropTypes.func
};
export class BugModal extends PureComponent {
render() {
const {
isOpen,
closeBugModal,
openIssueSearch,
createIssue
} = this.props;
return (
<Modal
show={ isOpen }
>
<Modal.Header className={ `${ns}-list-header` }>
Did you find a bug?
<span
className='close closing-x'
onClick={ closeBugModal }
>
×
</span>
</Modal.Header>
<Modal.Body className='text-center'>
<h3>
Before you submit a new issue,
read "How to Report a Bug" and
browse other issues with this challenge.
</h3>
<Button
block={ true }
bsSize='lg'
bsStyle='primary'
href={ bugLink }
target='_blank'
>
Read "How to Report a Bug"
</Button>
<Button
block={ true }
bsSize='lg'
bsStyle='primary'
onClick={ openIssueSearch }
>
Browse other issues with this challenge
</Button>
<Button
block={ true }
bsSize='lg'
bsStyle='primary'
onClick={ createIssue }
>
Create topic for issue in community forum
</Button>
<Button
block={ true }
bsSize='lg'
bsStyle='primary'
onClick={ closeBugModal }
>
Cancel
</Button>
</Modal.Body>
</Modal>
);
}
}
BugModal.displayName = 'BugModal';
BugModal.propTypes = propTypes;
export default connect(mapStateToProps, mapDispatchToProps)(BugModal);

View File

@@ -6,20 +6,18 @@ import { Button, Modal } from 'react-bootstrap';
import ns from './ns.json'; import ns from './ns.json';
import { import {
createQuestion, createQuestion,
openHelpChatRoom,
closeHelpModal, closeHelpModal,
helpModalSelector helpModalSelector
} from './redux'; } from './redux';
import { RSA } from '../../../utils/constantStrings.json'; import { RSA } from '../../../utils/constantStrings.json';
const mapStateToProps = state => ({ isOpen: helpModalSelector(state) }); const mapStateToProps = state => ({ isOpen: helpModalSelector(state) });
const mapDispatchToProps = { createQuestion, openHelpChatRoom, closeHelpModal }; const mapDispatchToProps = { createQuestion, closeHelpModal };
const propTypes = { const propTypes = {
closeHelpModal: PropTypes.func, closeHelpModal: PropTypes.func.isRequired,
createQuestion: PropTypes.func, createQuestion: PropTypes.func.isRequired,
isOpen: PropTypes.bool, isOpen: PropTypes.bool
openHelpChatRoom: PropTypes.func
}; };
export class HelpModal extends PureComponent { export class HelpModal extends PureComponent {
@@ -27,7 +25,6 @@ export class HelpModal extends PureComponent {
const { const {
isOpen, isOpen,
closeHelpModal, closeHelpModal,
openHelpChatRoom,
createQuestion createQuestion
} = this.props; } = this.props;
return ( return (
@@ -44,20 +41,11 @@ export class HelpModal extends PureComponent {
</span> </span>
</Modal.Header> </Modal.Header>
<Modal.Body className='text-center'> <Modal.Body className='text-center'>
<h3> <h3 className={`${ns}-help-modal-heading`}>
If you've already tried the&nbsp;Read-Search-Ask&nbsp;method, If you've already tried the&nbsp;
then you can ask for help on the freeCodeCamp forum. <a href={ RSA } title='Read, search, ask'>Read-Search-Ask</a>&nbsp;
method, then you can ask for help on the freeCodeCamp forum.
</h3> </h3>
<Button
block={ true }
bsSize='lg'
bsStyle='primary'
href={ RSA }
onClick={ closeHelpModal }
target='_blank'
>
Learn about the Read-Search-Ask Methodology
</Button>
<Button <Button
block={ true } block={ true }
bsSize='lg' bsSize='lg'
@@ -66,14 +54,6 @@ export class HelpModal extends PureComponent {
> >
Create a help post on the forum Create a help post on the forum
</Button> </Button>
<Button
block={ true }
bsSize='lg'
bsStyle='primary'
onClick={ openHelpChatRoom }
>
Ask for help in the Gitter Chatroom
</Button>
<Button <Button
block={ true } block={ true }
bsSize='lg' bsSize='lg'

View File

@@ -6,7 +6,6 @@ import { connect } from 'react-redux';
import ns from './ns.json'; import ns from './ns.json';
import BugModal from './Bug-Modal.jsx';
import HelpModal from './Help-Modal.jsx'; import HelpModal from './Help-Modal.jsx';
import ToolPanel from './Tool-Panel.jsx'; import ToolPanel from './Tool-Panel.jsx';
import ChallengeTitle from './Challenge-Title.jsx'; import ChallengeTitle from './Challenge-Title.jsx';
@@ -14,7 +13,6 @@ import ChallengeDescription from './Challenge-Description.jsx';
import TestSuite from './Test-Suite.jsx'; import TestSuite from './Test-Suite.jsx';
import Output from './Output.jsx'; import Output from './Output.jsx';
import { import {
openBugModal,
openHelpModal, openHelpModal,
updateHint, updateHint,
executeChallenge, executeChallenge,
@@ -24,7 +22,8 @@ import {
testsSelector, testsSelector,
outputSelector, outputSelector,
hintIndexSelector, hintIndexSelector,
codeLockedSelector codeLockedSelector,
guideURLSelector
} from './redux'; } from './redux';
import { descriptionRegex } from './utils'; import { descriptionRegex } from './utils';
@@ -35,7 +34,6 @@ const mapDispatchToProps = {
makeToast, makeToast,
executeChallenge, executeChallenge,
updateHint, updateHint,
openBugModal,
openHelpModal, openHelpModal,
unlockUntrustedCode unlockUntrustedCode
}; };
@@ -46,13 +44,15 @@ const mapStateToProps = createSelector(
outputSelector, outputSelector,
hintIndexSelector, hintIndexSelector,
codeLockedSelector, codeLockedSelector,
guideURLSelector,
( (
{ description, guideUrl }, { description },
{ title }, { title },
tests, tests,
output, output,
hintIndex, hintIndex,
isCodeLocked, isCodeLocked,
guideUrl
) => ({ ) => ({
title, title,
guideUrl, guideUrl,
@@ -69,7 +69,6 @@ const propTypes = {
hint: PropTypes.string, hint: PropTypes.string,
isCodeLocked: PropTypes.bool, isCodeLocked: PropTypes.bool,
makeToast: PropTypes.func, makeToast: PropTypes.func,
openBugModal: PropTypes.func,
openHelpModal: PropTypes.func, openHelpModal: PropTypes.func,
output: PropTypes.string, output: PropTypes.string,
tests: PropTypes.arrayOf(PropTypes.object), tests: PropTypes.arrayOf(PropTypes.object),
@@ -126,7 +125,6 @@ export class SidePanel extends PureComponent {
executeChallenge, executeChallenge,
updateHint, updateHint,
makeToast, makeToast,
openBugModal,
openHelpModal, openHelpModal,
isCodeLocked, isCodeLocked,
unlockUntrustedCode, unlockUntrustedCode,
@@ -153,12 +151,10 @@ export class SidePanel extends PureComponent {
hint={ hint } hint={ hint }
isCodeLocked={ isCodeLocked } isCodeLocked={ isCodeLocked }
makeToast={ makeToast } makeToast={ makeToast }
openBugModal={ openBugModal }
openHelpModal={ openHelpModal } openHelpModal={ openHelpModal }
unlockUntrustedCode={ unlockUntrustedCode } unlockUntrustedCode={ unlockUntrustedCode }
updateHint={ updateHint } updateHint={ updateHint }
/> />
<BugModal />
<HelpModal /> <HelpModal />
<Output <Output
defaultOutput={ defaultOutput={

View File

@@ -16,7 +16,6 @@ const propTypes = {
hint: PropTypes.string, hint: PropTypes.string,
isCodeLocked: PropTypes.bool, isCodeLocked: PropTypes.bool,
makeToast: PropTypes.func.isRequired, makeToast: PropTypes.func.isRequired,
openBugModal: PropTypes.func.isRequired,
openHelpModal: PropTypes.func.isRequired, openHelpModal: PropTypes.func.isRequired,
unlockUntrustedCode: PropTypes.func.isRequired, unlockUntrustedCode: PropTypes.func.isRequired,
updateHint: PropTypes.func.isRequired updateHint: PropTypes.func.isRequired
@@ -97,7 +96,6 @@ export default class ToolPanel extends PureComponent {
guideUrl, guideUrl,
hint, hint,
isCodeLocked, isCodeLocked,
openBugModal,
openHelpModal, openHelpModal,
unlockUntrustedCode unlockUntrustedCode
} = this.props; } = this.props;
@@ -122,36 +120,28 @@ export default class ToolPanel extends PureComponent {
Reset your code Reset your code
</Button> </Button>
<div className='button-spacer' /> <div className='button-spacer' />
{
guideUrl &&
<div>
<Button
block={ true }
bsStyle='primary'
className='btn-big'
href={ guideUrl }
target='_blank'
>
Get a hint
</Button>
<div className='button-spacer' />
</div>
}
<Button <Button
block={ true } block={ true }
bsStyle='primary' bsStyle='primary'
className='btn-big' className='btn-big'
onClick={ openHelpModal } onClick={ openHelpModal }
> >
Get Help Ask for help on the forum
</Button>
<div className='button-spacer' />
{guideUrl &&
<div>
<Button
block={ true }
bsStyle='primary'
className='btn-big'
href={ guideUrl }
target='_blank'
>
See Guide
</Button>
<div className='button-spacer' />
</div>
}
<Button
block={ true }
bsStyle='primary'
className='btn-big'
onClick={ openBugModal }
>
Report a Bug
</Button> </Button>
<div className='button-spacer' /> <div className='button-spacer' />
</div> </div>

View File

@@ -234,4 +234,8 @@
width: 100%; width: 100%;
} }
.@{ns}-help-modal-heading > a {
font-size: inherit;
}
&{ @import "./views/index.less"; } &{ @import "./views/index.less"; }

View File

@@ -77,17 +77,10 @@ export const types = createTypes([
createAsyncTypes('submitChallenge'), createAsyncTypes('submitChallenge'),
'moveToNextChallenge', 'moveToNextChallenge',
// bug
'openBugModal',
'closeBugModal',
'openIssueSearch',
'createIssue',
// help // help
'openHelpModal', 'openHelpModal',
'closeHelpModal', 'closeHelpModal',
'createQuestion', 'createQuestion',
'openHelpChatRoom',
// panes // panes
'toggleClassicEditor', 'toggleClassicEditor',
@@ -157,17 +150,10 @@ export const submitChallengeComplete = createAction(
export const moveToNextChallenge = createAction(types.moveToNextChallenge); export const moveToNextChallenge = createAction(types.moveToNextChallenge);
// bug
export const openBugModal = createAction(types.openBugModal);
export const closeBugModal = createAction(types.closeBugModal);
export const openIssueSearch = createAction(types.openIssueSearch);
export const createIssue = createAction(types.createIssue);
// help // help
export const openHelpModal = createAction(types.openHelpModal); export const openHelpModal = createAction(types.openHelpModal);
export const closeHelpModal = createAction(types.closeHelpModal); export const closeHelpModal = createAction(types.closeHelpModal);
export const createQuestion = createAction(types.createQuestion); export const createQuestion = createAction(types.createQuestion);
export const openHelpChatRoom = createAction(types.openHelpChatRoom);
// code storage // code storage
export const storedCodeFound = createAction( export const storedCodeFound = createAction(
@@ -221,8 +207,9 @@ export const challengeModalSelector =
challengeSelector(state).type !== 'backend' challengeSelector(state).type !== 'backend'
); );
export const bugModalSelector = state => getNS(state).isBugOpen;
export const helpModalSelector = state => getNS(state).isHelpOpen; export const helpModalSelector = state => getNS(state).isHelpOpen;
export const guideURLSelector = state =>
`https://guide.freecodecamp.org/certificates/${getNS(state).challenge}`;
export const challengeRequiredSelector = state => export const challengeRequiredSelector = state =>
challengeSelector(state).required || []; challengeSelector(state).required || [];
@@ -334,8 +321,6 @@ export default combineReducers(
...state, ...state,
output: (state.output || '') + output output: (state.output || '') + output
}), }),
[types.openBugModal]: state => ({ ...state, isBugOpen: true }),
[types.closeBugModal]: state => ({ ...state, isBugOpen: false }),
[types.openHelpModal]: state => ({ ...state, isHelpOpen: true }), [types.openHelpModal]: state => ({ ...state, isHelpOpen: true }),
[types.closeHelpModal]: state => ({ ...state, isHelpOpen: false }) [types.closeHelpModal]: state => ({ ...state, isHelpOpen: false })
}), }),

View File

@@ -1,8 +1,6 @@
import { combineEpics, ofType } from 'redux-epic'; import { ofType } from 'redux-epic';
import { import {
types, types,
chatRoomSelector,
closeBugModal,
closeHelpModal closeHelpModal
} from '../redux'; } from '../redux';
@@ -30,81 +28,6 @@ function filesToMarkdown(files = {}) {
}, '\n'); }, '\n');
} }
export function openIssueSearchEpic(actions, { getState }, { window }) {
return actions::ofType(types.openIssueSearch).map(() => {
const state = getState();
const challengeName = currentChallengeSelector(state);
window.open(
'https://forum.freecodecamp.org/search?q=' +
window.encodeURIComponent(challengeName)
);
return closeBugModal();
});
}
export function createIssueEpic(actions, { getState }, { window }) {
return actions::ofType(types.createIssue).map(() => {
const state = getState();
const files = filesSelector(state);
const challengeName = currentChallengeSelector(state);
const {
navigator: { userAgent },
location: { href }
} = window;
const titleText = 'Need assistance in ' + challengeName;
let textMessage = [
'#### Challenge Name\n',
'[',
challengeName,
'](',
href,
') has an issue.\n',
'#### Issue Description\n',
'<!-- Describe below when the issue happens and how to ',
'reproduce it -->\n\n\n',
'#### Browser Information\n',
'<!-- Describe your workspace in which you are having issues-->\n',
'User Agent is: <code>',
userAgent,
'</code>.\n\n',
'#### Screenshot\n',
'<!-- Add a screenshot of your issue -->\n\n\n',
'#### Your Code'
].join('');
const body = filesToMarkdown(files);
if (body.length > 10) {
textMessage += body;
}
window.open(
'https://forum.freecodecamp.org/new-topic'
+ '?category=General'
+ '&title=' + window.encodeURIComponent(titleText)
+ '&body=' + window.encodeURIComponent(textMessage),
'_blank'
);
return closeBugModal();
});
}
export function openHelpChatRoomEpic(actions, { getState }, { window }) {
return actions::ofType(types.openHelpChatRoom).map(() => {
const state = getState();
const helpChatRoom = chatRoomSelector(state);
window.open(
'https://gitter.im/freecodecamp/' +
window.encodeURIComponent(helpChatRoom)
);
return closeHelpModal();
});
}
export function createQuestionEpic(actions, { getState }, { window }) { export function createQuestionEpic(actions, { getState }, { window }) {
return actions::ofType(types.createQuestion).map(() => { return actions::ofType(types.createQuestion).map(() => {
const state = getState(); const state = getState();
@@ -138,9 +61,4 @@ export function createQuestionEpic(actions, { getState }, { window }) {
}); });
} }
export default combineEpics( export default createQuestionEpic;
openIssueSearchEpic,
createIssueEpic,
openHelpChatRoomEpic,
createQuestionEpic
);

View File

@@ -6,7 +6,7 @@ import { Col, Image } from 'react-bootstrap';
import SidePanel from './Side-Panel.jsx'; import SidePanel from './Side-Panel.jsx';
import ToolPanel from './Tool-Panel.jsx'; import ToolPanel from './Tool-Panel.jsx';
import BugModal from '../../Bug-Modal.jsx'; import HelpModal from '../../Help-Modal.jsx';
import { challengeMetaSelector } from '../../redux'; import { challengeMetaSelector } from '../../redux';
import { challengeSelector } from '../../../../redux'; import { challengeSelector } from '../../../../redux';
@@ -45,7 +45,7 @@ export class Project extends PureComponent {
isCompleted, isCompleted,
description description
} = this.props; } = this.props;
const imageURL = '//i.imgur.com/' + image + '.png'; const imageURL = 'https://i.imgur.com/' + image + '.png';
return ( return (
<Col <Col
md={ 8 } md={ 8 }
@@ -63,8 +63,7 @@ export class Project extends PureComponent {
/> />
<br /> <br />
<ToolPanel /> <ToolPanel />
<br /> <HelpModal />
<BugModal />
</Col> </Col>
); );
} }

View File

@@ -13,9 +13,10 @@ import { submittingSelector } from './redux';
import { import {
submitChallenge, submitChallenge,
openBugModal,
chatRoomSelector openHelpModal,
chatRoomSelector,
guideURLSelector
} from '../../redux'; } from '../../redux';
import { import {
@@ -28,32 +29,36 @@ import {
} from '../../../../utils/challengeTypes'; } from '../../../../utils/challengeTypes';
const propTypes = { const propTypes = {
guideUrl: PropTypes.string,
helpChatRoom: PropTypes.string.isRequired, helpChatRoom: PropTypes.string.isRequired,
isFrontEnd: PropTypes.bool, isFrontEnd: PropTypes.bool,
isSignedIn: PropTypes.bool, isSignedIn: PropTypes.bool,
isSimple: PropTypes.bool, isSimple: PropTypes.bool,
isSubmitting: PropTypes.bool, isSubmitting: PropTypes.bool,
openBugModal: PropTypes.func.isRequired, openHelpModal: PropTypes.func.isRequired,
submitChallenge: PropTypes.func.isRequired submitChallenge: PropTypes.func.isRequired
}; };
const mapDispatchToProps = { const mapDispatchToProps = {
submitChallenge, openHelpModal,
openBugModal submitChallenge
}; };
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
challengeSelector, challengeSelector,
signInLoadingSelector, signInLoadingSelector,
submittingSelector, submittingSelector,
chatRoomSelector, chatRoomSelector,
guideURLSelector,
( (
{ challengeType = simpleProject }, { challengeType = simpleProject },
showLoading, showLoading,
isSubmitting, isSubmitting,
helpChatRoom, helpChatRoom,
guideUrl
) => ({ ) => ({
guideUrl,
helpChatRoom,
isSignedIn: !showLoading, isSignedIn: !showLoading,
isSubmitting, isSubmitting,
helpChatRoom,
isSimple: challengeType === simpleProject, isSimple: challengeType === simpleProject,
isFrontEnd: challengeType === frontEndProject isFrontEnd: challengeType === frontEndProject
}) })
@@ -78,13 +83,14 @@ export class ToolPanel extends PureComponent {
render() { render() {
const { const {
guideUrl,
helpChatRoom,
isFrontEnd, isFrontEnd,
isSimple, isSimple,
isSignedIn, isSignedIn,
isSubmitting, isSubmitting,
helpChatRoom, openHelpModal,
submitChallenge, submitChallenge
openBugModal
} = this.props; } = this.props;
const FormElement = isFrontEnd ? FrontEndForm : BackEndForm; const FormElement = isFrontEnd ? FrontEndForm : BackEndForm;
@@ -96,7 +102,7 @@ export class ToolPanel extends PureComponent {
<FormElement isSubmitting={ isSubmitting }/> <FormElement isSubmitting={ isSubmitting }/>
} }
<div className='button-spacer' /> <div className='button-spacer' />
<ButtonGroup justified={ true }> <ButtonGroup vertical={ true }>
<Button <Button
bsStyle='primary' bsStyle='primary'
className='btn-primary-ghost btn-big' className='btn-primary-ghost btn-big'
@@ -107,12 +113,21 @@ export class ToolPanel extends PureComponent {
Help Help
</Button> </Button>
<Button <Button
block={ true }
bsStyle='primary' bsStyle='primary'
className='btn-primary-ghost btn-big' className='btn-primary-ghost btn-big'
componentClass='div' href={ guideUrl }
onClick={ openBugModal } target='_blank'
> >
Bug Get a hint
</Button>
<Button
block={ true }
bsStyle='primary'
className='btn-primary-ghost btn-big'
onClick={ openHelpModal }
>
Ask for help on the forum
</Button> </Button>
</ButtonGroup> </ButtonGroup>
</div> </div>