fix: project views and icons for intros (#36811)

This commit is contained in:
mrugesh
2019-09-19 12:44:58 +05:30
committed by Ahmad Abdolsaheb
parent 93360f4e01
commit 5d92b3c668
16 changed files with 155 additions and 166 deletions

View File

@ -0,0 +1,49 @@
import React, { Fragment } from 'react';
const propTypes = {};
function IntroInformation(props) {
return (
<Fragment>
<span className='sr-only'>IntroInformation</span>
<svg
height='50'
viewBox='0 0 200 200'
width='50'
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<g>
<title>IntroInformation</title>
<circle
cx='100'
cy='99'
fill='var(--primary-background)'
r='95'
stroke='var(--primary-color)'
strokeDasharray='null'
strokeLinecap='null'
strokeLinejoin='null'
strokeWidth='10'
/>
<circle
cx='100'
cy='99'
fill='var(--primary-color)'
r='45'
stroke='var(--primary-color)'
strokeDasharray='null'
strokeLinecap='null'
strokeLinejoin='null'
strokeWidth='10'
/>
</g>
</svg>
</Fragment>
);
}
IntroInformation.displayName = 'IntroInformation';
IntroInformation.propTypes = propTypes;
export default IntroInformation;

View File

@ -10,10 +10,10 @@ import { makeExpandedBlockSelector, toggleBlock } from '../redux';
import { completedChallengesSelector } from '../../../redux'; import { completedChallengesSelector } from '../../../redux';
import Caret from '../../../assets/icons/Caret'; import Caret from '../../../assets/icons/Caret';
import { blockNameify } from '../../../../utils/blockNameify'; import { blockNameify } from '../../../../utils/blockNameify';
/* eslint-disable max-len */
import GreenPass from '../../../assets/icons/GreenPass'; import GreenPass from '../../../assets/icons/GreenPass';
import GreenNotCompleted from '../../../assets/icons/GreenNotCompleted'; import GreenNotCompleted from '../../../assets/icons/GreenNotCompleted';
/* eslint-enable max-len */ import IntroInformation from '../../../assets/icons/IntroInformation';
const mapStateToProps = (state, ownProps) => { const mapStateToProps = (state, ownProps) => {
const expandedSelector = makeExpandedBlockSelector(ownProps.blockDashedName); const expandedSelector = makeExpandedBlockSelector(ownProps.blockDashedName);
@ -95,7 +95,11 @@ export class Block extends Component {
key={'map-challenge' + challenge.fields.slug} key={'map-challenge' + challenge.fields.slug}
> >
<span className='badge map-badge'> <span className='badge map-badge'>
{i !== 0 && this.renderCheckMark(challenge.isCompleted)} {i === 0 ? (
<IntroInformation style={mapIconStyle} />
) : (
this.renderCheckMark(challenge.isCompleted)
)}
</span> </span>
<Link <Link
onClick={this.handleChallengeClick(challenge.fields.slug)} onClick={this.handleChallengeClick(challenge.fields.slug)}

View File

@ -74,7 +74,17 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
> >
<span <span
className="badge map-badge" className="badge map-badge"
/> >
<IntroInformation
style={
Object {
"height": "15px",
"marginRight": "10px",
"width": "15px",
}
}
/>
</span>
<mockConstructor <mockConstructor
onClick={[Function]} onClick={[Function]}
to="/super-block-one/block-a" to="/super-block-one/block-a"

View File

@ -15,7 +15,7 @@ const propTypes = {
fields: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, fields: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
options: PropTypes.shape({ options: PropTypes.shape({
ignored: PropTypes.arrayOf(PropTypes.string), ignored: PropTypes.arrayOf(PropTypes.string),
placeholder: PropTypes.bool, placeholders: PropTypes.objectOf(PropTypes.string),
required: PropTypes.arrayOf(PropTypes.string), required: PropTypes.arrayOf(PropTypes.string),
types: PropTypes.objectOf(PropTypes.string) types: PropTypes.objectOf(PropTypes.string)
}) })
@ -25,7 +25,7 @@ function FormFields(props) {
const { fields, options = {} } = props; const { fields, options = {} } = props;
const { const {
ignored = [], ignored = [],
placeholder = true, placeholders = {},
required = [], required = [],
types = {} types = {}
} = options; } = options;
@ -38,6 +38,8 @@ function FormFields(props) {
{({ input: { value, onChange }, meta: { pristine, error } }) => { {({ input: { value, onChange }, meta: { pristine, error } }) => {
const key = kebabCase(name); const key = kebabCase(name);
const type = name in types ? types[name] : 'text'; const type = name in types ? types[name] : 'text';
const placeholder =
name in placeholders ? placeholders[name] : '';
return ( return (
<Col key={key} xs={12}> <Col key={key} xs={12}>
<FormGroup> <FormGroup>
@ -51,7 +53,7 @@ function FormFields(props) {
id={key} id={key}
name={name} name={name}
onChange={onChange} onChange={onChange}
placeholder={placeholder ? name : ''} placeholder={placeholder}
required={required.includes(name)} required={required.includes(name)}
rows={4} rows={4}
type={type} type={type}

View File

@ -98,47 +98,3 @@ export default connect(
mapStateToProps, mapStateToProps,
mapDispatchToProps mapDispatchToProps
)(ToolPanel); )(ToolPanel);
/*
<Button
block={true}
bsStyle='default'
className='btn-big'
onClick={executeChallenge}
>
Run tests (Ctrl + Enter)
</Button>
<div className='button-spacer' />
<Button
block={true}
bsStyle='default'
className='btn-big'
onClick={openResetModal}
>
Reset this lesson
</Button>
<div className='button-spacer' />
{guideUrl && (
<div>
<Button
block={true}
bsStyle='default'
className='btn-big'
href={guideUrl}
target='_blank'
>
Get a hint
</Button>
<div className='button-spacer' />
</div>
)}
<Button
block={true}
bsStyle='default'
className='btn-big'
onClick={openHelpModal}
>
Ask for help on the forum
</Button>
<div className='button-spacer' />
*/

View File

@ -1,44 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import ChallengeTitle from '../components/Challenge-Title';
import Spacer from '../../../components/helpers/Spacer';
const propTypes = {
description: PropTypes.string,
introPath: PropTypes.string,
isCompleted: PropTypes.bool,
isSignedIn: PropTypes.bool,
nextChallengePath: PropTypes.string,
prevChallengePath: PropTypes.string,
showPrevNextBtns: PropTypes.bool,
title: PropTypes.string
};
export default function SidePanel({
title,
description,
introPath,
isCompleted,
nextChallengePath,
prevChallengePath,
showPrevNextBtns
}) {
return (
<div>
<Spacer />
<ChallengeTitle
introPath={introPath}
isCompleted={isCompleted}
nextChallengePath={nextChallengePath}
prevChallengePath={prevChallengePath}
showPrevNextBtns={showPrevNextBtns}
>
{title}
</ChallengeTitle>
<div dangerouslySetInnerHTML={{ __html: description }} />
</div>
);
}
SidePanel.displayName = 'ProjectSidePanel';
SidePanel.propTypes = propTypes;

View File

@ -1,9 +0,0 @@
.project-show-wrapper {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
max-width: 960px;
margin: 0 auto;
padding: 0 15px;
}

View File

@ -49,7 +49,18 @@ export class ProjectForm extends Component {
buttonText={`${buttonCopy}`} buttonText={`${buttonCopy}`}
formFields={isFrontEnd ? frontEndFields : backEndFields} formFields={isFrontEnd ? frontEndFields : backEndFields}
id={isFrontEnd ? 'front-end-form' : 'back-end-form'} id={isFrontEnd ? 'front-end-form' : 'back-end-form'}
options={options} options={{
...options,
placeholders: {
solution:
'Link to solution, ex: ' +
(isFrontEnd
? 'https://codepen.io/camperbot/full/oNvPqqo'
: 'https://camperbot.glitch.me'),
githubLink:
'Link to GitHub repo, ex: https://github.com/camperbot/hello'
}
}}
submit={this.handleSubmit} submit={this.handleSubmit}
/> />
); );

View File

@ -4,6 +4,7 @@ import { Grid, Col, Row } from '@freecodecamp/react-bootstrap';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import Helmet from 'react-helmet';
import { import {
executeChallenge, executeChallenge,
@ -16,26 +17,26 @@ import {
updateChallengeMeta, updateChallengeMeta,
updateProjectFormValues, updateProjectFormValues,
backendNS backendNS
} from '../redux'; } from '../../redux';
import { getGuideUrl } from '../utils'; import { getGuideUrl } from '../../utils';
import LearnLayout from '../../../components/layouts/Learn'; import LearnLayout from '../../../../components/layouts/Learn';
import ChallengeTitle from '../components/Challenge-Title'; import ChallengeTitle from '../../components/Challenge-Title';
import ChallengeDescription from '../components/Challenge-Description'; import ChallengeDescription from '../../components/Challenge-Description';
import TestSuite from '../components/Test-Suite'; import TestSuite from '../../components/Test-Suite';
import Output from '../components/Output'; import Output from '../../components/Output';
import CompletionModal from '../components/CompletionModal'; import CompletionModal from '../../components/CompletionModal';
import HelpModal from '../components/HelpModal'; import HelpModal from '../../components/HelpModal';
import ProjectToolPanel from '../project/Tool-Panel'; import ProjectToolPanel from '../Tool-Panel';
import ProjectForm from '../project/ProjectForm'; import ProjectForm from '../ProjectForm';
import { Form } from '../../../components/formHelpers'; import { Form } from '../../../../components/formHelpers';
import Spacer from '../../../components/helpers/Spacer'; import Spacer from '../../../../components/helpers/Spacer';
import { ChallengeNode } from '../../../redux/propTypes'; import { ChallengeNode } from '../../../../redux/propTypes';
import { isSignedInSelector } from '../../../redux'; import { isSignedInSelector } from '../../../../redux';
import { backend } from '../../../../utils/challengeTypes'; import { backend } from '../../../../../utils/challengeTypes';
import '../components/test-frame.css'; import '../../components/test-frame.css';
const propTypes = { const propTypes = {
challengeMounted: PropTypes.func.isRequired, challengeMounted: PropTypes.func.isRequired,
@ -86,6 +87,9 @@ const options = {
required: ['solution'], required: ['solution'],
types: { types: {
solution: 'url' solution: 'url'
},
placeholders: {
solution: 'Link to solution, ex: https://codepen.io/camperbot/full/oNvPqqo'
} }
}; };
@ -181,6 +185,7 @@ export class BackEnd extends Component {
return ( return (
<LearnLayout> <LearnLayout>
<Helmet title={`${blockNameTitle} | Learn | freeCodeCamp.org`} />
<Grid> <Grid>
<Row> <Row>
<Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}> <Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
@ -228,7 +233,6 @@ export class BackEnd extends Component {
output={output} output={output}
/> />
<TestSuite tests={tests} /> <TestSuite tests={tests} />
<Spacer /> <Spacer />
</Col> </Col>
<CompletionModal /> <CompletionModal />

View File

@ -1,29 +1,29 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Grid, Col, Row } from '@freecodecamp/react-bootstrap';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { graphql } from 'gatsby'; import { graphql } from 'gatsby';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { ChallengeNode } from '../../../redux/propTypes'; import { ChallengeNode } from '../../../../redux/propTypes';
import { import {
challengeMounted, challengeMounted,
updateChallengeMeta, updateChallengeMeta,
openModal, openModal,
updateProjectFormValues updateProjectFormValues
} from '../redux'; } from '../../redux';
import { frontEndProject } from '../../../../utils/challengeTypes'; import { frontEndProject } from '../../../../../utils/challengeTypes';
import { getGuideUrl } from '../utils'; import { getGuideUrl } from '../../utils';
import LearnLayout from '../../../components/layouts/Learn'; import LearnLayout from '../../../../components/layouts/Learn';
import Spacer from '../../../components/helpers/Spacer'; import ChallengeTitle from '../../components/Challenge-Title';
import ProjectForm from './ProjectForm'; import ChallengeDescription from '../../components/Challenge-Description';
import SidePanel from './Side-Panel'; import Spacer from '../../../../components/helpers/Spacer';
import ToolPanel from './Tool-Panel'; import ProjectForm from '../ProjectForm';
import CompletionModal from '../components/CompletionModal'; import ProjectToolPanel from '../Tool-Panel';
import HelpModal from '../components/HelpModal'; import CompletionModal from '../../components/CompletionModal';
import HelpModal from '../../components/HelpModal';
import './project.css';
const mapStateToProps = () => ({}); const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
@ -96,8 +96,7 @@ export class Project extends Component {
fields: { blockName }, fields: { blockName },
forumTopicId, forumTopicId,
title, title,
description, description
guideUrl
} }
}, },
openCompletionModal, openCompletionModal,
@ -111,28 +110,35 @@ export class Project extends Component {
const blockNameTitle = `${blockName} - ${title}`; const blockNameTitle = `${blockName} - ${title}`;
return ( return (
<LearnLayout> <LearnLayout>
<Helmet title={`${blockNameTitle} | Learn freeCodeCamp}`} /> <Helmet title={`${blockNameTitle} | Learn | freeCodeCamp.org`} />
<div className='project-show-wrapper'> <Grid>
<SidePanel <Row>
className='full-height' <Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
description={description} <Spacer />
guideUrl={guideUrl} <ChallengeTitle
introPath={introPath} introPath={introPath}
nextChallengePath={nextChallengePath} nextChallengePath={nextChallengePath}
prevChallengePath={prevChallengePath} prevChallengePath={prevChallengePath}
showPrevNextBtns={true} showPrevNextBtns={true}
title={blockNameTitle} >
/> {blockNameTitle}
<ProjectForm </ChallengeTitle>
isFrontEnd={isFrontEnd} <ChallengeDescription description={description} />
onSubmit={openCompletionModal} <ProjectForm
updateProjectForm={updateProjectFormValues} isFrontEnd={isFrontEnd}
/> onSubmit={openCompletionModal}
<ToolPanel guideUrl={getGuideUrl({ forumTopicId, title })} /> updateProjectForm={updateProjectFormValues}
<Spacer /> />
</div> <ProjectToolPanel
<CompletionModal /> guideUrl={getGuideUrl({ forumTopicId, title })}
<HelpModal /> />
<br />
<Spacer />
</Col>
<CompletionModal />
<HelpModal />
</Row>
</Grid>
</LearnLayout> </LearnLayout>
); );
} }

View File

@ -57,7 +57,7 @@ function IntroductionPage({ data: { markdownRemark, allChallengeNode } }) {
Go to the first lesson Go to the first lesson
</Link> </Link>
<ButtonSpacer /> <ButtonSpacer />
<Link class='btn btn-lg btn-primary btn-block' to='/learn'> <Link className='btn btn-lg btn-primary btn-block' to='/learn'>
View the curriculum View the curriculum
</Link> </Link>
<ButtonSpacer /> <ButtonSpacer />

View File

@ -39,7 +39,7 @@ exports.viewTypes = {
[html]: 'classic', [html]: 'classic',
[js]: 'classic', [js]: 'classic',
[bonfire]: 'classic', [bonfire]: 'classic',
[frontEndProject]: 'project', [frontEndProject]: 'frontend',
[backEndProject]: 'backend', [backEndProject]: 'backend',
[modern]: 'modern', [modern]: 'modern',
[step]: 'step', [step]: 'step',

View File

@ -5,15 +5,15 @@ const { viewTypes } = require('../challengeTypes');
const backend = path.resolve( const backend = path.resolve(
__dirname, __dirname,
'../../src/templates/Challenges/backend/Show.js' '../../src/templates/Challenges/projects/backend/Show.js'
); );
const classic = path.resolve( const classic = path.resolve(
__dirname, __dirname,
'../../src/templates/Challenges/classic/Show.js' '../../src/templates/Challenges/classic/Show.js'
); );
const project = path.resolve( const frontend = path.resolve(
__dirname, __dirname,
'../../src/templates/Challenges/project/Show.js' '../../src/templates/Challenges/projects/frontend/Show.js'
); );
const intro = path.resolve( const intro = path.resolve(
__dirname, __dirname,
@ -28,7 +28,7 @@ const views = {
backend, backend,
classic, classic,
modern: classic, modern: classic,
project frontend
// quiz: Quiz // quiz: Quiz
}; };