feat(tool-panel): Make buttons look and act like current productiion (#16580)
parent
4ce84166ed
commit
e3a522aa5e
|
@ -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);
|
|
@ -6,20 +6,18 @@ import { Button, Modal } from 'react-bootstrap';
|
|||
import ns from './ns.json';
|
||||
import {
|
||||
createQuestion,
|
||||
openHelpChatRoom,
|
||||
closeHelpModal,
|
||||
helpModalSelector
|
||||
} from './redux';
|
||||
import { RSA } from '../../../utils/constantStrings.json';
|
||||
|
||||
const mapStateToProps = state => ({ isOpen: helpModalSelector(state) });
|
||||
const mapDispatchToProps = { createQuestion, openHelpChatRoom, closeHelpModal };
|
||||
const mapDispatchToProps = { createQuestion, closeHelpModal };
|
||||
|
||||
const propTypes = {
|
||||
closeHelpModal: PropTypes.func,
|
||||
createQuestion: PropTypes.func,
|
||||
isOpen: PropTypes.bool,
|
||||
openHelpChatRoom: PropTypes.func
|
||||
closeHelpModal: PropTypes.func.isRequired,
|
||||
createQuestion: PropTypes.func.isRequired,
|
||||
isOpen: PropTypes.bool
|
||||
};
|
||||
|
||||
export class HelpModal extends PureComponent {
|
||||
|
@ -27,7 +25,6 @@ export class HelpModal extends PureComponent {
|
|||
const {
|
||||
isOpen,
|
||||
closeHelpModal,
|
||||
openHelpChatRoom,
|
||||
createQuestion
|
||||
} = this.props;
|
||||
return (
|
||||
|
@ -44,20 +41,11 @@ export class HelpModal extends PureComponent {
|
|||
</span>
|
||||
</Modal.Header>
|
||||
<Modal.Body className='text-center'>
|
||||
<h3>
|
||||
If you've already tried the Read-Search-Ask method,
|
||||
then you can ask for help on the freeCodeCamp forum.
|
||||
<h3 className={`${ns}-help-modal-heading`}>
|
||||
If you've already tried the
|
||||
<a href={ RSA } title='Read, search, ask'>Read-Search-Ask</a>
|
||||
method, then you can ask for help on the freeCodeCamp forum.
|
||||
</h3>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
href={ RSA }
|
||||
onClick={ closeHelpModal }
|
||||
target='_blank'
|
||||
>
|
||||
Learn about the Read-Search-Ask Methodology
|
||||
</Button>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='lg'
|
||||
|
@ -66,14 +54,6 @@ export class HelpModal extends PureComponent {
|
|||
>
|
||||
Create a help post on the forum
|
||||
</Button>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='lg'
|
||||
bsStyle='primary'
|
||||
onClick={ openHelpChatRoom }
|
||||
>
|
||||
Ask for help in the Gitter Chatroom
|
||||
</Button>
|
||||
<Button
|
||||
block={ true }
|
||||
bsSize='lg'
|
||||
|
|
|
@ -6,7 +6,6 @@ import { connect } from 'react-redux';
|
|||
|
||||
import ns from './ns.json';
|
||||
|
||||
import BugModal from './Bug-Modal.jsx';
|
||||
import HelpModal from './Help-Modal.jsx';
|
||||
import ToolPanel from './Tool-Panel.jsx';
|
||||
import ChallengeTitle from './Challenge-Title.jsx';
|
||||
|
@ -14,7 +13,6 @@ import ChallengeDescription from './Challenge-Description.jsx';
|
|||
import TestSuite from './Test-Suite.jsx';
|
||||
import Output from './Output.jsx';
|
||||
import {
|
||||
openBugModal,
|
||||
openHelpModal,
|
||||
updateHint,
|
||||
executeChallenge,
|
||||
|
@ -24,7 +22,8 @@ import {
|
|||
testsSelector,
|
||||
outputSelector,
|
||||
hintIndexSelector,
|
||||
codeLockedSelector
|
||||
codeLockedSelector,
|
||||
guideURLSelector
|
||||
} from './redux';
|
||||
|
||||
import { descriptionRegex } from './utils';
|
||||
|
@ -35,7 +34,6 @@ const mapDispatchToProps = {
|
|||
makeToast,
|
||||
executeChallenge,
|
||||
updateHint,
|
||||
openBugModal,
|
||||
openHelpModal,
|
||||
unlockUntrustedCode
|
||||
};
|
||||
|
@ -46,13 +44,15 @@ const mapStateToProps = createSelector(
|
|||
outputSelector,
|
||||
hintIndexSelector,
|
||||
codeLockedSelector,
|
||||
guideURLSelector,
|
||||
(
|
||||
{ description, guideUrl },
|
||||
{ description },
|
||||
{ title },
|
||||
tests,
|
||||
output,
|
||||
hintIndex,
|
||||
isCodeLocked,
|
||||
guideUrl
|
||||
) => ({
|
||||
title,
|
||||
guideUrl,
|
||||
|
@ -69,7 +69,6 @@ const propTypes = {
|
|||
hint: PropTypes.string,
|
||||
isCodeLocked: PropTypes.bool,
|
||||
makeToast: PropTypes.func,
|
||||
openBugModal: PropTypes.func,
|
||||
openHelpModal: PropTypes.func,
|
||||
output: PropTypes.string,
|
||||
tests: PropTypes.arrayOf(PropTypes.object),
|
||||
|
@ -126,7 +125,6 @@ export class SidePanel extends PureComponent {
|
|||
executeChallenge,
|
||||
updateHint,
|
||||
makeToast,
|
||||
openBugModal,
|
||||
openHelpModal,
|
||||
isCodeLocked,
|
||||
unlockUntrustedCode,
|
||||
|
@ -153,12 +151,10 @@ export class SidePanel extends PureComponent {
|
|||
hint={ hint }
|
||||
isCodeLocked={ isCodeLocked }
|
||||
makeToast={ makeToast }
|
||||
openBugModal={ openBugModal }
|
||||
openHelpModal={ openHelpModal }
|
||||
unlockUntrustedCode={ unlockUntrustedCode }
|
||||
updateHint={ updateHint }
|
||||
/>
|
||||
<BugModal />
|
||||
<HelpModal />
|
||||
<Output
|
||||
defaultOutput={
|
||||
|
|
|
@ -16,7 +16,6 @@ const propTypes = {
|
|||
hint: PropTypes.string,
|
||||
isCodeLocked: PropTypes.bool,
|
||||
makeToast: PropTypes.func.isRequired,
|
||||
openBugModal: PropTypes.func.isRequired,
|
||||
openHelpModal: PropTypes.func.isRequired,
|
||||
unlockUntrustedCode: PropTypes.func.isRequired,
|
||||
updateHint: PropTypes.func.isRequired
|
||||
|
@ -97,7 +96,6 @@ export default class ToolPanel extends PureComponent {
|
|||
guideUrl,
|
||||
hint,
|
||||
isCodeLocked,
|
||||
openBugModal,
|
||||
openHelpModal,
|
||||
unlockUntrustedCode
|
||||
} = this.props;
|
||||
|
@ -122,36 +120,28 @@ export default class ToolPanel extends PureComponent {
|
|||
Reset your code
|
||||
</Button>
|
||||
<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
|
||||
block={ true }
|
||||
bsStyle='primary'
|
||||
className='btn-big'
|
||||
onClick={ openHelpModal }
|
||||
>
|
||||
Get Help
|
||||
</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
|
||||
Ask for help on the forum
|
||||
</Button>
|
||||
<div className='button-spacer' />
|
||||
</div>
|
||||
|
|
|
@ -234,4 +234,8 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.@{ns}-help-modal-heading > a {
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
&{ @import "./views/index.less"; }
|
||||
|
|
|
@ -77,17 +77,10 @@ export const types = createTypes([
|
|||
createAsyncTypes('submitChallenge'),
|
||||
'moveToNextChallenge',
|
||||
|
||||
// bug
|
||||
'openBugModal',
|
||||
'closeBugModal',
|
||||
'openIssueSearch',
|
||||
'createIssue',
|
||||
|
||||
// help
|
||||
'openHelpModal',
|
||||
'closeHelpModal',
|
||||
'createQuestion',
|
||||
'openHelpChatRoom',
|
||||
|
||||
// panes
|
||||
'toggleClassicEditor',
|
||||
|
@ -157,17 +150,10 @@ export const submitChallengeComplete = createAction(
|
|||
|
||||
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
|
||||
export const openHelpModal = createAction(types.openHelpModal);
|
||||
export const closeHelpModal = createAction(types.closeHelpModal);
|
||||
export const createQuestion = createAction(types.createQuestion);
|
||||
export const openHelpChatRoom = createAction(types.openHelpChatRoom);
|
||||
|
||||
// code storage
|
||||
export const storedCodeFound = createAction(
|
||||
|
@ -221,8 +207,9 @@ export const challengeModalSelector =
|
|||
challengeSelector(state).type !== 'backend'
|
||||
);
|
||||
|
||||
export const bugModalSelector = state => getNS(state).isBugOpen;
|
||||
export const helpModalSelector = state => getNS(state).isHelpOpen;
|
||||
export const guideURLSelector = state =>
|
||||
`https://guide.freecodecamp.org/certificates/${getNS(state).challenge}`;
|
||||
|
||||
export const challengeRequiredSelector = state =>
|
||||
challengeSelector(state).required || [];
|
||||
|
@ -334,8 +321,6 @@ export default combineReducers(
|
|||
...state,
|
||||
output: (state.output || '') + output
|
||||
}),
|
||||
[types.openBugModal]: state => ({ ...state, isBugOpen: true }),
|
||||
[types.closeBugModal]: state => ({ ...state, isBugOpen: false }),
|
||||
[types.openHelpModal]: state => ({ ...state, isHelpOpen: true }),
|
||||
[types.closeHelpModal]: state => ({ ...state, isHelpOpen: false })
|
||||
}),
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { combineEpics, ofType } from 'redux-epic';
|
||||
import { ofType } from 'redux-epic';
|
||||
import {
|
||||
types,
|
||||
chatRoomSelector,
|
||||
closeBugModal,
|
||||
closeHelpModal
|
||||
} from '../redux';
|
||||
|
||||
|
@ -30,81 +28,6 @@ function filesToMarkdown(files = {}) {
|
|||
}, '\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 }) {
|
||||
return actions::ofType(types.createQuestion).map(() => {
|
||||
const state = getState();
|
||||
|
@ -138,9 +61,4 @@ export function createQuestionEpic(actions, { getState }, { window }) {
|
|||
});
|
||||
}
|
||||
|
||||
export default combineEpics(
|
||||
openIssueSearchEpic,
|
||||
createIssueEpic,
|
||||
openHelpChatRoomEpic,
|
||||
createQuestionEpic
|
||||
);
|
||||
export default createQuestionEpic;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { Col, Image } from 'react-bootstrap';
|
|||
|
||||
import SidePanel from './Side-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 { challengeSelector } from '../../../../redux';
|
||||
|
@ -45,7 +45,7 @@ export class Project extends PureComponent {
|
|||
isCompleted,
|
||||
description
|
||||
} = this.props;
|
||||
const imageURL = '//i.imgur.com/' + image + '.png';
|
||||
const imageURL = 'https://i.imgur.com/' + image + '.png';
|
||||
return (
|
||||
<Col
|
||||
md={ 8 }
|
||||
|
@ -63,8 +63,7 @@ export class Project extends PureComponent {
|
|||
/>
|
||||
<br />
|
||||
<ToolPanel />
|
||||
<br />
|
||||
<BugModal />
|
||||
<HelpModal />
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ import { submittingSelector } from './redux';
|
|||
|
||||
import {
|
||||
submitChallenge,
|
||||
openBugModal,
|
||||
|
||||
chatRoomSelector
|
||||
openHelpModal,
|
||||
chatRoomSelector,
|
||||
guideURLSelector
|
||||
} from '../../redux';
|
||||
|
||||
import {
|
||||
|
@ -28,32 +29,36 @@ import {
|
|||
} from '../../../../utils/challengeTypes';
|
||||
|
||||
const propTypes = {
|
||||
guideUrl: PropTypes.string,
|
||||
helpChatRoom: PropTypes.string.isRequired,
|
||||
isFrontEnd: PropTypes.bool,
|
||||
isSignedIn: PropTypes.bool,
|
||||
isSimple: PropTypes.bool,
|
||||
isSubmitting: PropTypes.bool,
|
||||
openBugModal: PropTypes.func.isRequired,
|
||||
openHelpModal: PropTypes.func.isRequired,
|
||||
submitChallenge: PropTypes.func.isRequired
|
||||
};
|
||||
const mapDispatchToProps = {
|
||||
submitChallenge,
|
||||
openBugModal
|
||||
openHelpModal,
|
||||
submitChallenge
|
||||
};
|
||||
const mapStateToProps = createSelector(
|
||||
challengeSelector,
|
||||
signInLoadingSelector,
|
||||
submittingSelector,
|
||||
chatRoomSelector,
|
||||
guideURLSelector,
|
||||
(
|
||||
{ challengeType = simpleProject },
|
||||
showLoading,
|
||||
isSubmitting,
|
||||
helpChatRoom,
|
||||
guideUrl
|
||||
) => ({
|
||||
guideUrl,
|
||||
helpChatRoom,
|
||||
isSignedIn: !showLoading,
|
||||
isSubmitting,
|
||||
helpChatRoom,
|
||||
isSimple: challengeType === simpleProject,
|
||||
isFrontEnd: challengeType === frontEndProject
|
||||
})
|
||||
|
@ -78,13 +83,14 @@ export class ToolPanel extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
guideUrl,
|
||||
helpChatRoom,
|
||||
isFrontEnd,
|
||||
isSimple,
|
||||
isSignedIn,
|
||||
isSubmitting,
|
||||
helpChatRoom,
|
||||
submitChallenge,
|
||||
openBugModal
|
||||
openHelpModal,
|
||||
submitChallenge
|
||||
} = this.props;
|
||||
|
||||
const FormElement = isFrontEnd ? FrontEndForm : BackEndForm;
|
||||
|
@ -96,7 +102,7 @@ export class ToolPanel extends PureComponent {
|
|||
<FormElement isSubmitting={ isSubmitting }/>
|
||||
}
|
||||
<div className='button-spacer' />
|
||||
<ButtonGroup justified={ true }>
|
||||
<ButtonGroup vertical={ true }>
|
||||
<Button
|
||||
bsStyle='primary'
|
||||
className='btn-primary-ghost btn-big'
|
||||
|
@ -107,12 +113,21 @@ export class ToolPanel extends PureComponent {
|
|||
Help
|
||||
</Button>
|
||||
<Button
|
||||
block={ true }
|
||||
bsStyle='primary'
|
||||
className='btn-primary-ghost btn-big'
|
||||
componentClass='div'
|
||||
onClick={ openBugModal }
|
||||
href={ guideUrl }
|
||||
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>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue