fix: removed all guide bits from gatsby

This commit is contained in:
Mrugesh Mohapatra 2019-07-25 13:53:42 +05:30 committed by mrugesh
parent 7bc6367d68
commit 0124e7556d
27 changed files with 6 additions and 1493 deletions

View File

@ -6,16 +6,8 @@ const {
localeChallengesRootDir
} = require('./utils/buildChallenges');
const {
NODE_ENV: env,
LOCALE: locale = 'english',
API_PROXY: proxyUrl = 'http://localhost:3000'
} = process.env;
const { API_PROXY: proxyUrl = 'http://localhost:3000' } = process.env;
const selectedGuideDir = `../${
env === 'production' ? 'guide' : 'mock-guide'
}/${locale}`;
const guideRoot = path.resolve(__dirname, selectedGuideDir);
const curriculumIntroRoot = path.resolve(__dirname, './src/pages');
module.exports = {
@ -50,13 +42,6 @@ module.exports = {
curriculumPath: localeChallengesRootDir
}
},
{
resolve: 'gatsby-source-filesystem',
options: {
name: 'guides',
path: guideRoot
}
},
{
resolve: 'gatsby-source-filesystem',
options: {
@ -97,19 +82,6 @@ module.exports = {
]
}
},
{
resolve: 'gatsby-remark-node-identity',
options: {
identity: 'guideMarkdown',
predicate: ({ frontmatter }) => {
if (!frontmatter) {
return false;
}
const { title, block, superBlock } = frontmatter;
return title && !block && !superBlock;
}
}
},
{
resolve: 'gatsby-remark-node-identity',
options: {
@ -136,7 +108,6 @@ module.exports = {
}
}
},
{ resolve: 'fcc-create-nav-data' },
{
resolve: 'gatsby-plugin-manifest',
options: {

View File

@ -4,16 +4,13 @@ const { createFilePath } = require('gatsby-source-filesystem');
const { dasherize } = require('./utils');
const { blockNameify } = require('./utils/blockNameify');
const { getGithubPath } = require('./utils/getGithubPath');
const {
createChallengePages,
createBlockIntroPages,
createSuperBlockIntroPages,
createGuideArticlePages
createSuperBlockIntroPages
} = require('./utils/gatsby');
const createByIdentityMap = {
guideMarkdown: createGuideArticlePages,
blockIntroMarkdown: createBlockIntroPages,
superBlockIntroMarkdown: createSuperBlockIntroPages
};
@ -34,16 +31,10 @@ exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
const slug = createFilePath({ node, getNode });
if (!slug.includes('LICENSE')) {
const {
fileAbsolutePath,
frontmatter: { component = '' }
} = node;
createNodeField({ node, name: 'slug', value: slug });
createNodeField({ node, name: 'component', value: component });
createNodeField({
node,
name: 'githubPath',
value: getGithubPath(fileAbsolutePath)
});
}
}
};
@ -86,7 +77,6 @@ exports.createPages = function createPages({ graphql, actions }) {
slug
nodeIdentity
component
githubPath
}
frontmatter {
block

View File

@ -1,66 +0,0 @@
const crypto = require('crypto');
const path = require('path');
const commonREs = require('../../utils/regEx');
const readDir = require('../../utils/readDir');
const { isAStubRE } = commonREs;
// default locale to english for testing
const { NODE_ENV: env, LOCALE: locale = 'english' } = process.env;
const guideDir = `../../../${
env === 'production' ? 'guide' : 'mock-guide'
}/${locale}`;
const pagesDir = path.resolve(__dirname, guideDir);
const indexMdRe = new RegExp(`\\${path.sep}index.md$`);
function withGuidePrefix(str) {
return `/guide${str}`;
}
exports.createNavigationNode = function createNavigationNode(node) {
const {
fileAbsolutePath,
frontmatter: { title },
internal: { content },
parent
} = node;
const nodeDir = path.resolve(fileAbsolutePath).replace(indexMdRe, '');
const dashedName = nodeDir.split(path.sep).slice(-1)[0];
const nodePath = nodeDir
.split(pagesDir)[1]
.split(path.sep)
.join('/');
const parentPath = nodePath
.split('/')
.slice(0, -1)
.join('/');
const categoryChildren = readDir(nodeDir);
const navNode = {
categoryChildren,
hasChildren: !!categoryChildren.length,
dashedName,
isStubbed: isAStubRE.test(content),
path: withGuidePrefix(nodePath),
parentPath: withGuidePrefix(parentPath),
title
};
const gatsbyRequired = {
id: fileAbsolutePath + ' >>> NavigationNode',
parent,
children: [],
internal: {
type: 'NavigationNode',
contentDigest: crypto
.createHash('md5')
.update(JSON.stringify(navNode))
.digest('hex')
}
};
return { ...navNode, ...gatsbyRequired };
};

View File

@ -1,85 +0,0 @@
const path = require('path');
const { expect } = require('chai');
const { createNavigationNode } = require('./create-navigation-node');
describe('fcc-create-nav-data', () => {
describe('create-vanigation-node', () => {
const mockNode = {
internal: {
content:
'---\ntitle: File Writing\n---\n## File Writing\n\nThis is a stub.' +
" <a href='https://github.com/freeCodeCamp/guides/tree/master/src/" +
"pages/php/functions/files/writing/index.md' target='_blank' " +
"rel='nofollow'>Help our community expand it</a>.\n\n<a href=" +
"'https://github.com/freeCodeCamp/freeCodeCamp/blob/master/docs/style-guide-for-guide-articles.md' " +
"target='_blank' rel='nofollow'>This quick style guide will help " +
'ensure your pull request gets accepted</a>.\n\n<!-- The article ' +
'goes here, in GitHub-flavored Markdown. Feel free to add YouTube ' +
'videos, images, and CodePen/JSBin embeds -->\n\n#### More ' +
'Information:\n<!-- Please add any articles you think might be ' +
'helpful to read before writing the article -->\n'
},
frontmatter: {
title: 'File Writing'
},
fileAbsolutePath: path.resolve(
__dirname,
'../../../mock-guide/english/php/functions/files/file-writing/index.md'
)
};
const result = createNavigationNode(mockNode);
it('should return an object', () => {
expect(result).to.be.an('object');
});
it('node.children should be an array', () => {
expect(result.categoryChildren).to.be.an('array');
});
it('node.dashedName should equal the containing folder name', () => {
expect(result.dashedName).equal('file-writing');
});
it(
'node.path should equal the file path from pagesDir, ' +
'prefixed with `/guide`',
() => {
expect(result.path).to.equal('/guide/php/functions/files/file-writing');
}
);
it(
'node.parentPath should equal the path of the parent page, ' +
'prefixed with `/guide`',
() => {
expect(result.parentPath).to.equal('/guide/php/functions/files');
}
);
it('node.title should equal srcNode.frontmatter.title', () => {
expect(result.title).to.equal(mockNode.frontmatter.title);
});
it('node.isStubbed should be a boolean', () => {
expect(result.isStubbed).to.be.a('boolean');
});
it('node.isStubbed should be true for a stubbed article', () => {
expect(result.isStubbed).to.equal(true);
});
it('node.isStubbed should be false for a non-stubbed article', () => {
const notAStub = {
...mockNode,
internal: {
content: 'this is not a stub article. Infact, it is very informative'
}
};
const result = createNavigationNode(notAStub);
expect(result.isStubbed).to.equal(false);
});
});
});

View File

@ -1,21 +0,0 @@
const { createNavigationNode } = require('./create-navigation-node');
exports.onCreateNode = function createNavDataOnCreateNode({ actions, node }) {
const {
internal: { type },
fields
} = node;
if (
type === 'MarkdownRemark' &&
fields &&
fields.nodeIdentity === 'guideMarkdown'
) {
if (node.fileAbsolutePath.includes('LICENSE.md')) {
return null;
}
const { createNode } = actions;
const navNode = createNavigationNode(node);
return createNode(navNode);
}
return null;
};

View File

@ -1,218 +0,0 @@
{
"name": "fcc-create-nav-data",
"version": "0.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"assertion-error": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw=="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
}
},
"browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
},
"chai": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
"integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
"requires": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.2",
"deep-eql": "^3.0.1",
"get-func-name": "^2.0.0",
"pathval": "^1.1.0",
"type-detect": "^4.0.5"
}
},
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII="
},
"commander": {
"version": "2.15.1",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"deep-eql": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
"requires": {
"type-detect": "^4.0.0"
}
},
"diff": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
"integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"get-func-name": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE="
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"growl": {
"version": "1.10.5",
"resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"he": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"mocha": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
"integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
"requires": {
"browser-stdout": "1.3.1",
"commander": "2.15.1",
"debug": "3.1.0",
"diff": "3.5.0",
"escape-string-regexp": "1.0.5",
"glob": "7.1.2",
"growl": "1.10.5",
"he": "1.1.1",
"minimatch": "3.0.4",
"mkdirp": "0.5.1",
"supports-color": "5.4.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"pathval": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
"integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA="
},
"supports-color": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
"integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
"requires": {
"has-flag": "^3.0.0"
}
},
"type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

View File

@ -1,13 +0,0 @@
{
"name": "fcc-create-nav-data",
"private": true,
"version": "0.0.1",
"scripts": {
"test": "mocha -R spec \"./{,!(node_modules)/**/}*.test.js\"",
"test:watch": "mocha -R spec \"./{,!(node_modules)/**/}*.test.js\" --watch"
},
"dependencies": {
"chai": "^4.1.2",
"mocha": "^5.0.5"
}
}

View File

@ -1,131 +0,0 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { StaticQuery, graphql } from 'gatsby';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { createSelector } from 'reselect';
import { Grid, Col, Row } from '@freecodecamp/react-bootstrap';
import SideNav from './components/guide/SideNav';
import Spacer from '../helpers/Spacer';
import 'prismjs/themes/prism.css';
import './guide.css';
import {
toggleExpandedState,
toggleDisplayMenu,
displayMenuSelector,
expandedStateSelector
} from './components/guide/redux';
const propTypes = {
children: PropTypes.any,
data: PropTypes.shape({
allNavigationNode: PropTypes.shape({
edges: PropTypes.arrayOf(
PropTypes.shape({
node: PropTypes.shape({
dashedName: PropTypes.string,
isStubbed: PropTypes.bool,
path: PropTypes.string,
title: PropTypes.string
})
})
)
})
}),
displayMenu: PropTypes.bool,
expandedState: PropTypes.object,
location: PropTypes.object,
toggleDisplayMenu: PropTypes.func,
toggleExpandedState: PropTypes.func
};
const mapStateToProps = createSelector(
displayMenuSelector,
expandedStateSelector,
(displayMenu, expandedState) => ({
displayMenu,
expandedState
})
);
const mapDispatchToProps = dispatch =>
bindActionCreators({ toggleExpandedState, toggleDisplayMenu }, dispatch);
class GuideLayout extends React.Component {
getContentRef = ref => (this.contentRef = ref);
handleNavigation = () => {};
hideSideNav = () => {
if (this.props.displayMenu) {
this.props.toggleDisplayMenu();
}
};
render() {
const { expandedState, toggleExpandedState, displayMenu } = this.props;
return (
<StaticQuery
query={graphql`
query GuideLayoutQuery {
allNavigationNode {
edges {
node {
dashedName
hasChildren
isStubbed
parentPath
path
title
}
}
}
}
`}
render={data => {
const { edges } = data.allNavigationNode;
const pages = edges.map(edge => edge.node);
return (
<Fragment>
<Spacer size={1} />
<Grid className='guide-container'>
<Row>
<Col md={4} smHidden={!displayMenu} xsHidden={!displayMenu}>
<SideNav
expandedState={expandedState}
onNavigate={this.handleNavigation}
pages={pages}
toggleDisplaySideNav={this.hideSideNav}
toggleExpandedState={toggleExpandedState}
/>
</Col>
<Col md={8} smHidden={displayMenu} xsHidden={displayMenu}>
<main
className='content'
id='main'
ref={this.getContentRef}
tabIndex='-1'
>
{this.props.children}
</main>
</Col>
</Row>
</Grid>
</Fragment>
);
}}
/>
);
}
}
GuideLayout.displayName = 'GuideLayout';
GuideLayout.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(GuideLayout);

View File

@ -1,36 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
const propTypes = {
isStubbed: PropTypes.bool,
onNavigate: PropTypes.func.isRequired,
path: PropTypes.string,
router: PropTypes.object,
title: PropTypes.string,
toggleDisplaySideNav: PropTypes.func.isRequired
};
class NavItem extends React.Component {
handleClick = () => {
this.props.toggleDisplaySideNav();
this.props.onNavigate();
};
render() {
const { isStubbed, path, title } = this.props;
return (
<li>
<Link onClick={this.handleClick} to={path}>
<span className={'navItemTitle' + (isStubbed ? ' stubbed' : '')}>
{title}
</span>
</Link>
</li>
);
}
}
NavItem.displayName = 'NavItem';
NavItem.propTypes = propTypes;
export default NavItem;

View File

@ -1,45 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { createSelector } from 'reselect';
import MenuButton from '../../../../components/Header/components/MenuButton';
import MenuLinks from '../../../../components/Header/components/MenuLinks';
import { toggleDisplayMenu, displayMenuSelector } from './redux';
const mapStateToProps = createSelector(
displayMenuSelector,
displayMenu => ({
displayMenu
})
);
const mapDispatchToProps = dispatch =>
bindActionCreators({ toggleDisplayMenu }, dispatch);
function GuideNavigationMenu(props) {
const { displayMenu, toggleDisplayMenu } = props;
return (
<>
<MenuButton
className={'guide-menu-button'}
displayMenu={displayMenu}
onClick={toggleDisplayMenu}
/>
<MenuLinks className={'guide-top-nav'} />
</>
);
}
GuideNavigationMenu.displayName = 'GuideNavigationMenu';
GuideNavigationMenu.propTypes = {
displayMenu: PropTypes.bool.isRequired,
toggleDisplayMenu: PropTypes.func.isRequired
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(GuideNavigationMenu);

View File

@ -1,106 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Panel } from '@freecodecamp/react-bootstrap';
import { Link } from 'gatsby';
const propTypes = {
children: PropTypes.any,
dashedName: PropTypes.string,
handleClick: PropTypes.func.isRequired,
hasChildren: PropTypes.bool,
isExpanded: PropTypes.bool,
onNavigate: PropTypes.func.isRequired,
path: PropTypes.string,
title: PropTypes.string,
toggleDisplaySideNav: PropTypes.func.isRequired
};
function NoArticles() {
return (
<li>
<span>
No articles yet.
<br />
Could you&nbsp;
<a
href={
'https://github.com/freeCodeCamp/freeCodeCamp/blob/master/docs/' +
'how-to-work-on-guide-articles.md#how-to-work-on-guide-articles'
}
rel='noopener noreferrer'
target='_blank'
>
write one?
</a>
</span>
</li>
);
}
class NavPanel extends Component {
constructor() {
super();
this.renderHeader = this.renderHeader.bind(this);
this.handleHeaderClick = this.handleHeaderClick.bind(this);
this.handleTitleClick = this.handleTitleClick.bind(this);
this.renderBody = this.renderBody.bind(this);
}
handleHeaderClick() {
const { path, handleClick } = this.props;
handleClick(path);
}
handleTitleClick() {
const { toggleDisplaySideNav, onNavigate } = this.props;
toggleDisplaySideNav();
onNavigate();
}
renderHeader() {
const { isExpanded, path, title } = this.props;
return (
<div className='title' onClick={this.handleHeaderClick}>
<span
className={
'caret ' + (isExpanded ? 'caretStyle expanded' : 'caretStyle')
}
/>
<Link onClick={this.handleTitleClick} to={path}>
{title}
</Link>
</div>
);
}
renderBody() {
const { hasChildren, children, isExpanded } = this.props;
return (
<div className={isExpanded ? 'body' : ''}>
<ul className='navPanelUl'>
{hasChildren ? children : <NoArticles />}
</ul>
</div>
);
}
render() {
const { isExpanded, dashedName } = this.props;
return (
<Panel
bsClass='panelStyle panel'
id={`${dashedName}-panel`}
role='listitem'
>
<Panel.Heading>{this.renderHeader()}</Panel.Heading>
{isExpanded ? <Panel.Body>{this.renderBody()}</Panel.Body> : null}
</Panel>
);
}
}
NavPanel.displayName = 'NavPanel';
NavPanel.propTypes = propTypes;
export default NavPanel;

View File

@ -1,108 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import PanelGroup from '@freecodecamp/react-bootstrap/lib/PanelGroup';
import NavPanel from './NavPanel';
import NavItem from './NavItem';
const propTypes = {
expandedState: PropTypes.object,
onNavigate: PropTypes.func.isRequired,
pages: PropTypes.arrayOf(PropTypes.object),
parents: PropTypes.arrayOf(PropTypes.object),
toggleDisplaySideNav: PropTypes.func.isRequired,
toggleExpandedState: PropTypes.func.isRequired
};
function parentFilter(node) {
return node.path.split('/').filter(Boolean).length === 2;
}
class SideNav extends Component {
constructor(...props) {
super(...props);
this.renderChildren = this.renderChildren.bind(this);
this.renderPanels = this.renderPanels.bind(this);
this.renderParent = this.renderParent.bind(this);
}
renderPanels(parents, pages) {
if (!parents) {
return 'No Parents Here';
}
return parents.map(parent => this.renderParent(parent, pages));
}
renderParent(parent, pages) {
const childrenForParent = pages.filter(
page => page.parentPath === parent.path
);
const { path } = parent;
const isExpanded = !!this.props.expandedState[path];
const [category] = pages.filter(page => page.path === path);
const { title, hasChildren, dashedName } = category;
const children = isExpanded
? this.renderChildren(childrenForParent, pages)
: null;
return (
<NavPanel
dashedName={dashedName}
handleClick={this.props.toggleExpandedState}
hasChildren={hasChildren}
isExpanded={isExpanded}
key={parent.path}
onNavigate={this.props.onNavigate}
path={parent.path}
title={title}
toggleDisplaySideNav={this.props.toggleDisplaySideNav}
>
{children}
</NavPanel>
);
}
renderChildren(children, pages) {
return children.map(child => {
if (child.hasChildren) {
return this.renderParent(child, pages);
}
return (
<NavItem
isStubbed={child.isStubbed}
key={child.path}
onNavigate={this.props.onNavigate}
path={child.path}
title={child.title}
toggleDisplaySideNav={this.props.toggleDisplaySideNav}
/>
);
});
}
render() {
const { pages, expandedState } = this.props;
const parents = pages.filter(parentFilter);
const panels = this.renderPanels(parents, pages);
return (
<nav className='sideNav' id='side-nav'>
<PanelGroup id='guide-navigation-side-panel' role='list'>
{!parents || !expandedState ? (
<NavPanel
title='No Parents Here'
toggleDisplaySideNav={this.props.toggleDisplaySideNav}
/>
) : (
panels
)}
</PanelGroup>
</nav>
);
}
}
SideNav.displayName = 'SideNav';
SideNav.propTypes = propTypes;
export default SideNav;

View File

@ -1,38 +0,0 @@
import { createAction, handleActions } from 'redux-actions';
import { createTypes } from '../../../../../utils/createTypes';
import { createSideNavigationSaga } from './side-navigation-saga';
export const ns = 'guideNav';
const initialState = {
displayMenu: false,
expandedState: {}
};
const types = createTypes(['toggleExpandedState', 'toggleDisplayMenu'], ns);
export const sagas = [...createSideNavigationSaga(types)];
export const toggleExpandedState = createAction(types.toggleExpandedState);
export const toggleDisplayMenu = createAction(types.toggleDisplayMenu);
export const displayMenuSelector = state => state[ns].displayMenu;
export const expandedStateSelector = state => state[ns].expandedState;
export const reducer = handleActions(
{
[types.toggleExpandedState]: (state, { payload }) => ({
...state,
expandedState: {
...state.expandedState,
[payload]: !state.expandedState[payload]
}
}),
[types.toggleDisplayMenu]: state => ({
...state,
displayMenu: !state.displayMenu
})
},
initialState
);

View File

@ -1,31 +0,0 @@
import { takeEvery, put, all } from 'redux-saga/effects';
import { toggleExpandedState } from './';
import { types as appTypes } from '../../../../../redux';
function* expandSideNavOnMountSaga() {
let guideRegex = /^\/guide\//;
let onGuide = guideRegex.test(window.location.pathname);
if (onGuide && typeof window !== 'undefined') {
const pathMap = window.location.pathname
.slice(1)
.split('/')
.slice(0, -1)
.reduce((map, current, i, pathArray) => {
const path =
i !== 0 ? map[pathArray[i - 1]] + `/${current}` : `/${current}`;
return {
...map,
[current]: path
};
}, {});
let routes = Object.keys(pathMap).map(key => pathMap[key]);
yield all(routes.map(route => put(toggleExpandedState(route))));
}
}
export function createSideNavigationSaga() {
return [takeEvery(appTypes.appMount, expandSideNavOnMountSaga)];
}

View File

@ -1,141 +0,0 @@
.guide-container {
font-size: 16px;
}
/* Breadcrumbs */
.breadcrumb {
display: inline-block;
padding: 5px 10px;
margin: 0;
margin-bottom: 5px;
opacity: 0.8;
transition: opacity 0.1s ease-out;
}
.breadcrumb:hover {
opacity: 1;
}
.breadcrumb > li a {
color: #006400;
}
.breadcrumb > li {
margin: 0;
}
.breadcrumb > li + li:before {
content: '\203A';
color: #555;
}
.breadcrumb > .active {
color: #555;
}
/* Layout */
.content,
.sideNav {
/* 100vh - (navbar height) - (spacer height) */
height: 100%;
overflow-y: auto;
}
.content img {
max-width: 100%;
display: block;
}
.content img.forum-image {
display: inline-block;
width: 20px;
height: 20px;
}
/* SideNav */
.caretStyle {
border-top: 8px dashed;
border-right: 6px solid transparent;
border-left: 6px solid transparent;
color: darkgray;
margin-right: 5px;
transform: rotate(-90deg);
transition: transform 150ms ease;
}
.caretStyle.expanded {
transform: rotate(0deg);
}
.navPanelUl {
list-style: none;
margin-bottom: 0;
}
.navPanelUl > li {
margin-bottom: 0;
}
.navItemTitle {
margin-left: 19px;
}
.panelStyle {
border: none !important;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.sideNav {
position: relative;
}
.stubbed {
color: #777;
}
.stubbed::after {
content: 'stub';
background-color: #aaa;
color: #fff;
border-radius: 3px;
padding: 0 4px;
margin-left: 5px;
line-height: 1;
}
.title {
background-color: white;
border: none;
border-radius: 0;
cursor: pointer;
}
.night .panelStyle,
.night .title {
background-color: #333;
}
@media (max-width: 992px) {
.content {
height: auto;
}
#top-right-nav.guide-top-nav {
display: none;
}
}
@media (min-width: 993px) {
#top-nav .guide-menu-button {
display: none;
}
}
.content,
.article {
outline: 0;
}

View File

@ -1,4 +1,3 @@
export { default as CertificationLayout } from './Certification';
export { default as DefaultLayout } from './Default';
export { default as GuideLayout } from './Guide';
export { default as LearnLayout } from './Learn';

View File

@ -1,53 +0,0 @@
import React, { Fragment } from 'react';
import Helmet from 'react-helmet';
import { Link } from '../components/helpers';
function Index() {
return (
<Fragment>
<Helmet>
<title>Guide | freeCodeCamp.org</title>
<meta
content='Short, concise guides and how-tos for the busy developer.'
name='description'
/>
</Helmet>
<h2>freeCodeCamp Guide</h2>
<p>
This website is full of articles about all things related to
programming. You can use the search bar above to find something would
like to learn about, or use the navigation to explore the content.
</p>
<p>There are articles on:</p>
<ul>
<li>SQL</li>
<li>Mathematics</li>
<li>JavaScript</li>
<li>Bootstrap</li>
<li>Git</li>
<li>and a whole lot more</li>
</ul>
<h3>Not sure where to start?</h3>
<p>
If you want to learn programming but you're not sure where to start,
check out <Link to='https://freecodecamp.org'>freeCodeCamp.org</Link>.
It has a curriculum that starts from zero and helps you learn to code.
</p>
<h3>Contribute to the Guide</h3>
<p>
This site and the articles on it are{' '}
<Link to='https://github.com/freeCodeCamp/freeCodeCamp'>
{' '}
open source{' '}
</Link>{' '}
. Your help in making it better is greatly appreciated!
</p>
<hr />
<p>Happy coding!</p>
</Fragment>
);
}
Index.displayName = 'IndexPage';
export default Index;

View File

@ -6,10 +6,6 @@ import {
reducer as flash,
ns as flashNameSpace
} from '../components/Flash/redux';
import {
reducer as guideNav,
ns as guideNavNameSpace
} from '../components/layouts/components/guide/redux';
import { reducer as settings, ns as settingsNameSpace } from './settings';
import {
reducer as curriculumMap,
@ -38,7 +34,6 @@ export default combineReducers({
[challengeNameSpace]: challenge,
[curriculumMapNameSpace]: curriculumMap,
[flashNameSpace]: flash,
[guideNavNameSpace]: guideNav,
form: formReducer,
[searchNameSpace]: search,
[settingsNameSpace]: settings

View File

@ -5,15 +5,6 @@ import { sagas as appSagas } from './';
import { sagas as challengeSagas } from '../templates/Challenges/redux';
import { sagas as settingsSagas } from './settings';
// eslint-disable-next-line max-len
import { sagas as sideNavigationSaga } from '../components/layouts/components/guide/redux';
export default function* rootSaga() {
yield all([
...errorSagas,
...appSagas,
...challengeSagas,
...settingsSagas,
...sideNavigationSaga
]);
yield all([...errorSagas, ...appSagas, ...challengeSagas, ...settingsSagas]);
}

View File

@ -1,41 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import ArticleLayout from './components/ArticleLayout';
const propTypes = {
data: PropTypes.object
};
const GuideArticle = props => {
const {
data: {
markdownRemark: { html }
}
} = props;
return (
<ArticleLayout {...props}>
<article
className='article'
dangerouslySetInnerHTML={{ __html: html }}
id='article'
tabIndex='-1'
/>
</ArticleLayout>
);
};
GuideArticle.displayName = 'GuideArticle';
GuideArticle.propTypes = propTypes;
export default GuideArticle;
export const pageQuery = graphql`
query ArticleById($id: String!) {
markdownRemark(id: { eq: $id }) {
html
...ArticleLayout
}
}
`;

View File

@ -1,72 +0,0 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import Helmet from 'react-helmet';
import Breadcrumbs from './Breadcrumbs';
import GuideFooter from './GuideFooter';
const propTypes = {
children: PropTypes.any,
data: PropTypes.object,
location: PropTypes.object,
pageContext: PropTypes.shape({
meta: PropTypes.objectOf(PropTypes.string)
})
};
const ArticleLayout = props => {
const {
children,
location: { pathname },
data: {
markdownRemark: {
fields: { slug, githubPath },
frontmatter: { title }
}
},
pageContext: { meta }
} = props;
return (
<Fragment>
<Helmet>
<title>{`${title} | freeCodeCamp.org`}</title>
<link href={`https://www.freecodecamp.org${slug}`} rel='canonical' />
<meta
content={`https://www.freecodecamp.org${slug}`}
property='og:url'
/>
<meta content={title} property='og:title' />
<meta
content={meta.description ? meta.description : ''}
property='og:description'
/>
<meta
content={meta.description ? meta.description : ''}
name='description'
/>
<meta content={meta.featureImage} property='og:image' />
</Helmet>
<Breadcrumbs path={pathname} />
{children}
<GuideFooter githubPath={githubPath} />
</Fragment>
);
};
ArticleLayout.displayName = 'ArticleLayout';
ArticleLayout.propTypes = propTypes;
export default ArticleLayout;
export const fragmentQuery = graphql`
fragment ArticleLayout on MarkdownRemark {
fields {
slug
githubPath
}
frontmatter {
title
}
}
`;

View File

@ -1,55 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Link from 'gatsby-link';
import titleify from '../../../../utils/titleify';
const propTypes = {
path: PropTypes.string.isRequired
};
function Breadcrumbs(props) {
const { path } = props;
if (path === '/') {
return null;
}
const pathMap = path
// remove leading and trailing slash
.replace(/^\/([a-z0-9/-]+[^/])\/?$/i, '$1')
.split('/')
.reduce((accu, current, i, pathArray) => {
const path =
i !== 0 ? accu[pathArray[i - 1]].path + `/${current}` : `/${current}`;
return {
...accu,
[current]: {
path,
title: titleify(current)
}
};
}, {});
const crumbs = Object.keys(pathMap)
.map(key => pathMap[key])
.map((page, i, thisArray) => {
if (i === thisArray.length - 1) {
return (
<li className='active' key={i}>
{page.title}
</li>
);
}
return (
<li key={i}>
<Link to={page.path}>{page.title}</Link>
</li>
);
});
return <ol className='breadcrumb'>{crumbs}</ol>;
}
Breadcrumbs.displayName = 'Breadcrumbs';
Breadcrumbs.propTypes = propTypes;
export default Breadcrumbs;

View File

@ -1,37 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link, Spacer } from '../../../components/helpers';
const propTypes = {
githubPath: PropTypes.string
};
const GuideFooter = ({ githubPath }) => (
<>
<Spacer />
<hr />
<h4>Contributing to the Guide</h4>
<p>
This open source guide is curated by thousands of contributors. You can
help by researching, writing and updating these articles. It is an easy
and fun way to get started with contributing to open source.
</p>
<ul>
<li>
<Link to='https://github.com/freeCodeCamp/freeCodeCamp/blob/master/CONTRIBUTING.md#research-write-and-update-our-guide-articles'>
Follow our contributing guidelines for working on guide articles
</Link>
.
</li>
<li>
<Link to={githubPath}>Edit this article on GitHub</Link>.
</li>
</ul>
<Spacer />
</>
);
GuideFooter.displayName = 'GuideFooter';
GuideFooter.propTypes = propTypes;
export default GuideFooter;

View File

@ -1,80 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { graphql } from 'gatsby';
import ArticleLayout from '../../ArticleLayout';
const propTypes = {
data: PropTypes.object
};
class CustomClickCounter extends React.Component {
state = {
init: false,
span: null,
div: null,
counter: 0
};
initComponent() {
const span = document.getElementById('example-component_span');
const div = document.getElementById('example-component_div');
this.setState({ init: true, span, div });
}
handleClick = () => {
this.setState(state => ({ counter: state.counter + 1 }));
};
componentDidMount() {
this.initComponent();
}
render() {
if (!this.state.init) {
return null;
}
return (
<div>
{ReactDOM.createPortal(this.state.counter, this.state.span)}
{ReactDOM.createPortal(
<button onClick={this.handleClick}>Click me!</button>,
this.state.div
)}
</div>
);
}
}
const ExampleComponent = props => {
const {
data: {
markdownRemark: { html }
}
} = props;
return (
<ArticleLayout {...props}>
<article
className='article'
dangerouslySetInnerHTML={{ __html: html }}
id='article'
tabIndex='-1'
/>
<CustomClickCounter />
</ArticleLayout>
);
};
ExampleComponent.displayName = 'ExampleComponent';
ExampleComponent.propTypes = propTypes;
export default ExampleComponent;
export const pageQuery = graphql`
query ExampleComponent($id: String!) {
markdownRemark(id: { eq: $id }) {
html
...ArticleLayout
}
}
`;

View File

@ -1,47 +0,0 @@
const path = require('path');
const select = require('unist-util-select');
const { head } = require('lodash');
const { isAStubRE } = require('../regEx');
const guideArticle = path.resolve(
__dirname,
'../../src/templates/Guide/GuideArticle.js'
);
exports.createGuideArticlePages = createPage => ({
node: {
htmlAst,
excerpt,
fields: { slug, component },
id
}
}) => {
let meta = {};
if (!isAStubRE.test(excerpt)) {
const featureImage = head(select(htmlAst, 'element[tagName=img]'));
meta.featureImage = featureImage
? featureImage.properties.src
: 'https://s3.amazonaws.com/freecodecamp' +
'/freecodecamp-square-logo-large.jpg';
const description = head(select(htmlAst, 'element[tagName=p]'));
meta.description = description ? description.children[0].value : '';
}
return createPage({
path: `/guide${slug}`,
component: !component
? guideArticle
: path.resolve(
__dirname,
'../../src/templates/Guide/components/',
component
),
context: {
id,
meta
}
});
};

View File

@ -1,7 +1,5 @@
const challengePageCreators = require('./challengePageCreator');
const guidePageCreators = require('./guidePageCreator');
module.exports = {
...challengePageCreators,
...guidePageCreators
...challengePageCreators
};

View File

@ -3,11 +3,8 @@ import PropTypes from 'prop-types';
import {
CertificationLayout,
DefaultLayout,
GuideLayout
DefaultLayout
} from '../../src/components/layouts';
// eslint-disable-next-line max-len
import GuideNavMenu from '../../src/components/layouts/components/guide/NavMenu';
import FourOhFourPage from '../../src/pages/404';
export default function layoutSelector({ element, props }) {
@ -28,11 +25,7 @@ export default function layoutSelector({ element, props }) {
return <CertificationLayout>{element}</CertificationLayout>;
}
if (/^\/guide(\/.*)*/.test(pathname)) {
return (
<DefaultLayout navigationMenu={<GuideNavMenu />} pathname={pathname}>
<GuideLayout>{element}</GuideLayout>
</DefaultLayout>
);
console.log('Hitting guide for some reason. Need a redirect.');
}
if (/^\/learn(\/.*)*/.test(pathname)) {
return (