fix: project views and icons for intros (#36811)
This commit is contained in:
committed by
Ahmad Abdolsaheb
parent
93360f4e01
commit
5d92b3c668
49
client/src/assets/icons/IntroInformation.js
Normal file
49
client/src/assets/icons/IntroInformation.js
Normal 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;
|
@ -10,10 +10,10 @@ import { makeExpandedBlockSelector, toggleBlock } from '../redux';
|
||||
import { completedChallengesSelector } from '../../../redux';
|
||||
import Caret from '../../../assets/icons/Caret';
|
||||
import { blockNameify } from '../../../../utils/blockNameify';
|
||||
/* eslint-disable max-len */
|
||||
import GreenPass from '../../../assets/icons/GreenPass';
|
||||
import GreenNotCompleted from '../../../assets/icons/GreenNotCompleted';
|
||||
/* eslint-enable max-len */
|
||||
import IntroInformation from '../../../assets/icons/IntroInformation';
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
const expandedSelector = makeExpandedBlockSelector(ownProps.blockDashedName);
|
||||
|
||||
@ -95,7 +95,11 @@ export class Block extends Component {
|
||||
key={'map-challenge' + challenge.fields.slug}
|
||||
>
|
||||
<span className='badge map-badge'>
|
||||
{i !== 0 && this.renderCheckMark(challenge.isCompleted)}
|
||||
{i === 0 ? (
|
||||
<IntroInformation style={mapIconStyle} />
|
||||
) : (
|
||||
this.renderCheckMark(challenge.isCompleted)
|
||||
)}
|
||||
</span>
|
||||
<Link
|
||||
onClick={this.handleChallengeClick(challenge.fields.slug)}
|
||||
|
@ -74,7 +74,17 @@ exports[`<Block expanded snapshot: block-expanded 1`] = `
|
||||
>
|
||||
<span
|
||||
className="badge map-badge"
|
||||
>
|
||||
<IntroInformation
|
||||
style={
|
||||
Object {
|
||||
"height": "15px",
|
||||
"marginRight": "10px",
|
||||
"width": "15px",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</span>
|
||||
<mockConstructor
|
||||
onClick={[Function]}
|
||||
to="/super-block-one/block-a"
|
||||
|
@ -15,7 +15,7 @@ const propTypes = {
|
||||
fields: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
|
||||
options: PropTypes.shape({
|
||||
ignored: PropTypes.arrayOf(PropTypes.string),
|
||||
placeholder: PropTypes.bool,
|
||||
placeholders: PropTypes.objectOf(PropTypes.string),
|
||||
required: PropTypes.arrayOf(PropTypes.string),
|
||||
types: PropTypes.objectOf(PropTypes.string)
|
||||
})
|
||||
@ -25,7 +25,7 @@ function FormFields(props) {
|
||||
const { fields, options = {} } = props;
|
||||
const {
|
||||
ignored = [],
|
||||
placeholder = true,
|
||||
placeholders = {},
|
||||
required = [],
|
||||
types = {}
|
||||
} = options;
|
||||
@ -38,6 +38,8 @@ function FormFields(props) {
|
||||
{({ input: { value, onChange }, meta: { pristine, error } }) => {
|
||||
const key = kebabCase(name);
|
||||
const type = name in types ? types[name] : 'text';
|
||||
const placeholder =
|
||||
name in placeholders ? placeholders[name] : '';
|
||||
return (
|
||||
<Col key={key} xs={12}>
|
||||
<FormGroup>
|
||||
@ -51,7 +53,7 @@ function FormFields(props) {
|
||||
id={key}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
placeholder={placeholder ? name : ''}
|
||||
placeholder={placeholder}
|
||||
required={required.includes(name)}
|
||||
rows={4}
|
||||
type={type}
|
||||
|
@ -98,47 +98,3 @@ export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(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' />
|
||||
*/
|
||||
|
@ -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;
|
@ -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;
|
||||
}
|
@ -49,7 +49,18 @@ export class ProjectForm extends Component {
|
||||
buttonText={`${buttonCopy}`}
|
||||
formFields={isFrontEnd ? frontEndFields : backEndFields}
|
||||
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}
|
||||
/>
|
||||
);
|
@ -4,6 +4,7 @@ import { Grid, Col, Row } from '@freecodecamp/react-bootstrap';
|
||||
import { createSelector } from 'reselect';
|
||||
import { connect } from 'react-redux';
|
||||
import { graphql } from 'gatsby';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
import {
|
||||
executeChallenge,
|
||||
@ -16,26 +17,26 @@ import {
|
||||
updateChallengeMeta,
|
||||
updateProjectFormValues,
|
||||
backendNS
|
||||
} from '../redux';
|
||||
import { getGuideUrl } from '../utils';
|
||||
} from '../../redux';
|
||||
import { getGuideUrl } from '../../utils';
|
||||
|
||||
import LearnLayout from '../../../components/layouts/Learn';
|
||||
import ChallengeTitle from '../components/Challenge-Title';
|
||||
import ChallengeDescription from '../components/Challenge-Description';
|
||||
import TestSuite from '../components/Test-Suite';
|
||||
import Output from '../components/Output';
|
||||
import CompletionModal from '../components/CompletionModal';
|
||||
import HelpModal from '../components/HelpModal';
|
||||
import ProjectToolPanel from '../project/Tool-Panel';
|
||||
import ProjectForm from '../project/ProjectForm';
|
||||
import { Form } from '../../../components/formHelpers';
|
||||
import Spacer from '../../../components/helpers/Spacer';
|
||||
import { ChallengeNode } from '../../../redux/propTypes';
|
||||
import { isSignedInSelector } from '../../../redux';
|
||||
import LearnLayout from '../../../../components/layouts/Learn';
|
||||
import ChallengeTitle from '../../components/Challenge-Title';
|
||||
import ChallengeDescription from '../../components/Challenge-Description';
|
||||
import TestSuite from '../../components/Test-Suite';
|
||||
import Output from '../../components/Output';
|
||||
import CompletionModal from '../../components/CompletionModal';
|
||||
import HelpModal from '../../components/HelpModal';
|
||||
import ProjectToolPanel from '../Tool-Panel';
|
||||
import ProjectForm from '../ProjectForm';
|
||||
import { Form } from '../../../../components/formHelpers';
|
||||
import Spacer from '../../../../components/helpers/Spacer';
|
||||
import { ChallengeNode } from '../../../../redux/propTypes';
|
||||
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 = {
|
||||
challengeMounted: PropTypes.func.isRequired,
|
||||
@ -86,6 +87,9 @@ const options = {
|
||||
required: ['solution'],
|
||||
types: {
|
||||
solution: 'url'
|
||||
},
|
||||
placeholders: {
|
||||
solution: 'Link to solution, ex: https://codepen.io/camperbot/full/oNvPqqo'
|
||||
}
|
||||
};
|
||||
|
||||
@ -181,6 +185,7 @@ export class BackEnd extends Component {
|
||||
|
||||
return (
|
||||
<LearnLayout>
|
||||
<Helmet title={`${blockNameTitle} | Learn | freeCodeCamp.org`} />
|
||||
<Grid>
|
||||
<Row>
|
||||
<Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
|
||||
@ -228,7 +233,6 @@ export class BackEnd extends Component {
|
||||
output={output}
|
||||
/>
|
||||
<TestSuite tests={tests} />
|
||||
|
||||
<Spacer />
|
||||
</Col>
|
||||
<CompletionModal />
|
@ -1,29 +1,29 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Grid, Col, Row } from '@freecodecamp/react-bootstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { graphql } from 'gatsby';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
import { ChallengeNode } from '../../../redux/propTypes';
|
||||
import { ChallengeNode } from '../../../../redux/propTypes';
|
||||
import {
|
||||
challengeMounted,
|
||||
updateChallengeMeta,
|
||||
openModal,
|
||||
updateProjectFormValues
|
||||
} from '../redux';
|
||||
import { frontEndProject } from '../../../../utils/challengeTypes';
|
||||
import { getGuideUrl } from '../utils';
|
||||
} from '../../redux';
|
||||
import { frontEndProject } from '../../../../../utils/challengeTypes';
|
||||
import { getGuideUrl } from '../../utils';
|
||||
|
||||
import LearnLayout from '../../../components/layouts/Learn';
|
||||
import Spacer from '../../../components/helpers/Spacer';
|
||||
import ProjectForm from './ProjectForm';
|
||||
import SidePanel from './Side-Panel';
|
||||
import ToolPanel from './Tool-Panel';
|
||||
import CompletionModal from '../components/CompletionModal';
|
||||
import HelpModal from '../components/HelpModal';
|
||||
|
||||
import './project.css';
|
||||
import LearnLayout from '../../../../components/layouts/Learn';
|
||||
import ChallengeTitle from '../../components/Challenge-Title';
|
||||
import ChallengeDescription from '../../components/Challenge-Description';
|
||||
import Spacer from '../../../../components/helpers/Spacer';
|
||||
import ProjectForm from '../ProjectForm';
|
||||
import ProjectToolPanel from '../Tool-Panel';
|
||||
import CompletionModal from '../../components/CompletionModal';
|
||||
import HelpModal from '../../components/HelpModal';
|
||||
|
||||
const mapStateToProps = () => ({});
|
||||
const mapDispatchToProps = dispatch =>
|
||||
@ -96,8 +96,7 @@ export class Project extends Component {
|
||||
fields: { blockName },
|
||||
forumTopicId,
|
||||
title,
|
||||
description,
|
||||
guideUrl
|
||||
description
|
||||
}
|
||||
},
|
||||
openCompletionModal,
|
||||
@ -111,28 +110,35 @@ export class Project extends Component {
|
||||
const blockNameTitle = `${blockName} - ${title}`;
|
||||
return (
|
||||
<LearnLayout>
|
||||
<Helmet title={`${blockNameTitle} | Learn freeCodeCamp}`} />
|
||||
<div className='project-show-wrapper'>
|
||||
<SidePanel
|
||||
className='full-height'
|
||||
description={description}
|
||||
guideUrl={guideUrl}
|
||||
<Helmet title={`${blockNameTitle} | Learn | freeCodeCamp.org`} />
|
||||
<Grid>
|
||||
<Row>
|
||||
<Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
|
||||
<Spacer />
|
||||
<ChallengeTitle
|
||||
introPath={introPath}
|
||||
nextChallengePath={nextChallengePath}
|
||||
prevChallengePath={prevChallengePath}
|
||||
showPrevNextBtns={true}
|
||||
title={blockNameTitle}
|
||||
/>
|
||||
>
|
||||
{blockNameTitle}
|
||||
</ChallengeTitle>
|
||||
<ChallengeDescription description={description} />
|
||||
<ProjectForm
|
||||
isFrontEnd={isFrontEnd}
|
||||
onSubmit={openCompletionModal}
|
||||
updateProjectForm={updateProjectFormValues}
|
||||
/>
|
||||
<ToolPanel guideUrl={getGuideUrl({ forumTopicId, title })} />
|
||||
<ProjectToolPanel
|
||||
guideUrl={getGuideUrl({ forumTopicId, title })}
|
||||
/>
|
||||
<br />
|
||||
<Spacer />
|
||||
</div>
|
||||
</Col>
|
||||
<CompletionModal />
|
||||
<HelpModal />
|
||||
</Row>
|
||||
</Grid>
|
||||
</LearnLayout>
|
||||
);
|
||||
}
|
@ -57,7 +57,7 @@ function IntroductionPage({ data: { markdownRemark, allChallengeNode } }) {
|
||||
Go to the first lesson
|
||||
</Link>
|
||||
<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
|
||||
</Link>
|
||||
<ButtonSpacer />
|
||||
|
@ -39,7 +39,7 @@ exports.viewTypes = {
|
||||
[html]: 'classic',
|
||||
[js]: 'classic',
|
||||
[bonfire]: 'classic',
|
||||
[frontEndProject]: 'project',
|
||||
[frontEndProject]: 'frontend',
|
||||
[backEndProject]: 'backend',
|
||||
[modern]: 'modern',
|
||||
[step]: 'step',
|
||||
|
@ -5,15 +5,15 @@ const { viewTypes } = require('../challengeTypes');
|
||||
|
||||
const backend = path.resolve(
|
||||
__dirname,
|
||||
'../../src/templates/Challenges/backend/Show.js'
|
||||
'../../src/templates/Challenges/projects/backend/Show.js'
|
||||
);
|
||||
const classic = path.resolve(
|
||||
__dirname,
|
||||
'../../src/templates/Challenges/classic/Show.js'
|
||||
);
|
||||
const project = path.resolve(
|
||||
const frontend = path.resolve(
|
||||
__dirname,
|
||||
'../../src/templates/Challenges/project/Show.js'
|
||||
'../../src/templates/Challenges/projects/frontend/Show.js'
|
||||
);
|
||||
const intro = path.resolve(
|
||||
__dirname,
|
||||
@ -28,7 +28,7 @@ const views = {
|
||||
backend,
|
||||
classic,
|
||||
modern: classic,
|
||||
project
|
||||
frontend
|
||||
// quiz: Quiz
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user