fix: all things (#124)

* fix 'Testing with Chai' 404 error

* Remove service worker and unregister old workers from clients

* Fix block name in QA and Chai intro

* Move the map to the index page

* Split test commands, have travis run format on test

* Only expand the first block

* Remove some margin from p tags on the index page

* Learn -> Curriculum link in header

* UI Fixes

* Use webpack plugin to destroy sercive worker

* Add spinner for unknown user state

* Fix map placement

* Fix vcurriculum button clicks
This commit is contained in:
mrugesh mohapatra
2018-06-01 03:36:42 +05:30
committed by Mrugesh Mohapatra
parent 6aff2bbe42
commit 910dd0584c
38 changed files with 330 additions and 341 deletions

View File

@@ -5,4 +5,5 @@ node_js:
- "lts/*" - "lts/*"
cache: cache:
directories: directories:
- "node_modules" - "node_modules"
script: "yarn test:ci"

View File

@@ -68,7 +68,6 @@ module.exports = {
fonts: ['Lato:400,400i,500'] fonts: ['Lato:400,400i,500']
} }
}, },
'gatsby-plugin-sitemap', 'gatsby-plugin-sitemap'
'gatsby-plugin-offline'
] ]
}; };

View File

@@ -104,6 +104,7 @@ exports.createPages = ({ graphql, boundActionCreators }) => {
}; };
const webpack = require('webpack'); const webpack = require('webpack');
const RmServiceWorkerPlugin = require('webpack-remove-serviceworker-plugin');
const generateBabelConfig = require('gatsby/dist/utils/babel-config'); const generateBabelConfig = require('gatsby/dist/utils/babel-config');
exports.modifyWebpackConfig = ({ config, stage }) => { exports.modifyWebpackConfig = ({ config, stage }) => {
@@ -143,6 +144,9 @@ exports.modifyWebpackConfig = ({ config, stage }) => {
) )
} }
]); ]);
config.plugin('RemoveServiceWorkerPlugin', RmServiceWorkerPlugin, [
{ filename: 'sw.js' }
]);
}); });
}; };
/* eslint-disable prefer-object-spread/prefer-object-spread */ /* eslint-disable prefer-object-spread/prefer-object-spread */

View File

@@ -23,7 +23,6 @@
"gatsby": "^1.9.243", "gatsby": "^1.9.243",
"gatsby-link": "^1.6.39", "gatsby-link": "^1.6.39",
"gatsby-plugin-google-fonts": "^0.0.4", "gatsby-plugin-google-fonts": "^0.0.4",
"gatsby-plugin-offline": "^1.0.15",
"gatsby-plugin-react-helmet": "^2.0.8", "gatsby-plugin-react-helmet": "^2.0.8",
"gatsby-plugin-react-next": "^1.0.11", "gatsby-plugin-react-next": "^1.0.11",
"gatsby-plugin-sitemap": "^1.2.21", "gatsby-plugin-sitemap": "^1.2.21",
@@ -48,6 +47,7 @@
"react-redux": "^5.0.7", "react-redux": "^5.0.7",
"react-reflex": "^2.2.1", "react-reflex": "^2.2.1",
"react-router-redux": "^5.0.0-alpha.9", "react-router-redux": "^5.0.0-alpha.9",
"react-spinkit": "^3.0.0",
"react-test-renderer": "^16.3.1", "react-test-renderer": "^16.3.1",
"redux": "^3.7.2", "redux": "^3.7.2",
"redux-actions": "^2.3.0", "redux-actions": "^2.3.0",
@@ -57,7 +57,8 @@
"rxjs": "^5.5.7", "rxjs": "^5.5.7",
"store": "^2.0.12", "store": "^2.0.12",
"uglifyjs-webpack-plugin": "^1.2.4", "uglifyjs-webpack-plugin": "^1.2.4",
"validator": "^9.4.1" "validator": "^9.4.1",
"webpack-remove-serviceworker-plugin": "^1.0.0"
}, },
"keywords": [ "keywords": [
"gatsby" "gatsby"
@@ -77,7 +78,8 @@
"lint:src": "eslint ./src . --fix", "lint:src": "eslint ./src . --fix",
"lint:utils": "eslint ./utils . --fix", "lint:utils": "eslint ./utils . --fix",
"pretty": "yarn format && yarn lint", "pretty": "yarn format && yarn lint",
"test": "yarn format && jest src", "test": "jest src",
"test:ci": "yarn format && jest src",
"test:watch": "jest --watch src" "test:watch": "jest --watch src"
}, },
"jest": { "jest": {

View File

@@ -2,24 +2,45 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import Spinner from 'react-spinkit';
import { isSignedInSelector } from '../../../redux/app'; import {
isSignedInSelector,
userStateLoadingSelector
} from '../../../redux/app';
import Login from './Login'; import Login from './Login';
import SignedIn from './SignedIn'; import SignedIn from './SignedIn';
const mapStateToProps = createSelector(isSignedInSelector, isSignedIn => ({ const mapStateToProps = createSelector(
isSignedIn userStateLoadingSelector,
})); isSignedInSelector,
(showLoading, isSignedIn) => ({
isSignedIn,
showLoading
})
);
const propTypes = { const propTypes = {
email: PropTypes.string, email: PropTypes.string,
isSignedIn: PropTypes.bool, isSignedIn: PropTypes.bool,
name: PropTypes.string name: PropTypes.string,
showLoading: PropTypes.bool
}; };
class UserState extends PureComponent { class UserState extends PureComponent {
render() { render() {
const { isSignedIn } = this.props; const { isSignedIn, showLoading } = this.props;
if (showLoading) {
return (
<Spinner
className='user-state-spinner'
color='white'
fadeIn='none'
height='40px'
name='line-scale'
/>
);
}
return isSignedIn ? <SignedIn /> : <Login />; return isSignedIn ? <SignedIn /> : <Login />;
} }
} }

View File

@@ -70,6 +70,13 @@ header {
font-weight: 500; font-weight: 500;
} }
.user-state-spinner {
height: 40px;
}
.user-state-spinner > div {
animation-duration: 1.5s !important;
}
/* Search bar */ /* Search bar */
.fcc_searchBar { .fcc_searchBar {

View File

@@ -17,7 +17,7 @@ function Header() {
<FCCSearch /> <FCCSearch />
<ul id='top-right-nav'> <ul id='top-right-nav'>
<li> <li>
<a href='https://learn.freecodecamp.org'>Learn</a> <Link to='/'>Curriculum</Link>
</li> </li>
<li> <li>
<a href='https://forum.freecodecamp.org'>Forum</a> <a href='https://forum.freecodecamp.org'>Forum</a>

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import Link, { navigateTo } from 'gatsby-link'; import Link from 'gatsby-link';
import ga from '../../../analytics'; import ga from '../../../analytics';
import { makeExpandedBlockSelector, toggleBlock } from '../redux'; import { makeExpandedBlockSelector, toggleBlock } from '../redux';
@@ -57,17 +57,12 @@ export class Block extends PureComponent {
} }
handleBlockClick() { handleBlockClick() {
const { blockDashedName, challenges, toggleBlock } = this.props; const { blockDashedName, toggleBlock } = this.props;
const blockPath = challenges[0].fields.slug
.split('/')
.slice(0, -1)
.join('/');
toggleBlock(blockDashedName);
ga.event({ ga.event({
category: 'Map Block Click', category: 'Map Block Click',
action: blockDashedName action: blockDashedName
}); });
return navigateTo(blockPath); return toggleBlock(blockDashedName);
} }
handleChallengeClick(slug) { handleChallengeClick(slug) {
@@ -80,7 +75,7 @@ export class Block extends PureComponent {
}; };
} }
renderChallenges(intro, challenges) { renderChallenges(intro = {}, challenges = []) {
// TODO: Split this into a Challenge Component and add tests // TODO: Split this into a Challenge Component and add tests
// TODO: The styles badge and map-badge on the completion span do not exist // TODO: The styles badge and map-badge on the completion span do not exist
return [intro].concat(challenges).map(challenge => { return [intro].concat(challenges).map(challenge => {

View File

@@ -45,6 +45,14 @@ const propTypes = {
toggleSuperBlock: PropTypes.func.isRequired toggleSuperBlock: PropTypes.func.isRequired
}; };
const codingPrepRE = new RegExp('Interview Prep');
function createSuperBlockTitle(str) {
return codingPrepRE.test(str)
? `${str} (Thousands of hours of challenges)`
: `${str} Certification (300 hours)`;
}
export class SuperBlock extends PureComponent { export class SuperBlock extends PureComponent {
renderBlock(superBlock) { renderBlock(superBlock) {
const { nodes, introNodes } = this.props; const { nodes, introNodes } = this.props;
@@ -54,7 +62,6 @@ export class SuperBlock extends PureComponent {
const blockDashedNames = uniq( const blockDashedNames = uniq(
blocksForSuperBlock.map(({ block }) => block) blocksForSuperBlock.map(({ block }) => block)
); );
return ( return (
<ul> <ul>
{blockDashedNames.map(blockDashedName => ( {blockDashedNames.map(blockDashedName => (
@@ -84,7 +91,7 @@ export class SuperBlock extends PureComponent {
<li className={`superblock ${isExpanded ? 'open' : ''}`}> <li className={`superblock ${isExpanded ? 'open' : ''}`}>
<div className='map-title' onClick={() => toggleSuperBlock(superBlock)}> <div className='map-title' onClick={() => toggleSuperBlock(superBlock)}>
<Caret /> <Caret />
<h4>{superBlock}</h4> <h4>{createSuperBlockTitle(superBlock)}</h4>
</div> </div>
{isExpanded ? this.renderBlock(superBlock) : null} {isExpanded ? this.renderBlock(superBlock) : null}
</li> </li>

View File

@@ -61,7 +61,7 @@ test('<SuperBlock should handle toggle clicks correctly', () => {
.find('.map-title') .find('.map-title')
.find('h4') .find('h4')
.text() .text()
).toBe('Super Block One'); ).toBe('Super Block One Certification (300 hours)');
expect(enzymeWrapper.find('ul').length).toBe(0); expect(enzymeWrapper.find('ul').length).toBe(0);
enzymeWrapper.find('.map-title').simulate('click'); enzymeWrapper.find('.map-title').simulate('click');
@@ -76,6 +76,6 @@ test('<SuperBlock should handle toggle clicks correctly', () => {
.find('.map-title') .find('.map-title')
.find('h4') .find('h4')
.text() .text()
).toBe('Super Block One'); ).toBe('Super Block One Certification (300 hours)');
expect(enzymeWrapper.find('ul').length).toBe(1); expect(enzymeWrapper.find('ul').length).toBe(1);
}); });

View File

@@ -10,7 +10,7 @@ exports[`<SuperBlock /> expanded snapshot: superBlock-expanded 1`] = `
> >
<Caret /> <Caret />
<h4> <h4>
Super Block One Super Block One Certification (300 hours)
</h4> </h4>
</div> </div>
<ul> <ul>
@@ -147,7 +147,7 @@ exports[`<SuperBlock /> not expanded snapshot: superBlock-not-expanded 1`] = `
> >
<Caret /> <Caret />
<h4> <h4>
Super Block One Super Block One Certification (300 hours)
</h4> </h4>
</div> </div>
</li> </li>

View File

@@ -1,6 +1,7 @@
.map-ui { .map-ui {
height: 100%; height: 100%;
max-height: calc(100vh - (45px + 1.45rem)); max-height: calc(100vh - (45px + 1.45rem));
margin-left: 35px;
} }
.map-ui ul { .map-ui ul {

View File

@@ -8,8 +8,12 @@ export const getNS = () => ns;
const initialState = { const initialState = {
expandedState: { expandedState: {
superBlock: {}, superBlock: {
block: {} 'Responsive Web Design': true
},
block: {
'basic-html-and-html5': true
}
} }
}; };

View File

@@ -1,3 +1,4 @@
.util-spacer { .util-spacer {
margin: 5px 0px; margin: 5px 0px;
height: 1px;
} }

View File

@@ -1,6 +1,6 @@
--- ---
title: Introduction to the Quality Assurance with Chai Challenges title: Introduction to the Quality Assurance with Chai Challenges
block: Quality Assurance with Chai block: Quality Assurance and Testing with Chai
superBlock: Information Security and Quality Assurance superBlock: Information Security and Quality Assurance
--- ---
## Introduction to Quality Assurance with Chai Challenges ## Introduction to Quality Assurance with Chai Challenges

View File

@@ -656,4 +656,4 @@ h1, h2, h3, h4, h5, h6 {
.btn-primary-link > a { .btn-primary-link > a {
color: #fff; color: #fff;
} }

View File

@@ -1,4 +1,3 @@
/* global graphql */
import React, { Fragment, PureComponent } from 'react'; import React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@@ -7,9 +6,7 @@ import Helmet from 'react-helmet';
import ga from '../analytics'; import ga from '../analytics';
import { AllChallengeNode } from '../redux/propTypes';
import Header from '../components/Header'; import Header from '../components/Header';
import MapModal from '../components/MapModal';
import { fetchUser } from '../redux/app'; import { fetchUser } from '../redux/app';
import 'prismjs/themes/prism.css'; import 'prismjs/themes/prism.css';
@@ -47,7 +44,6 @@ const mapDispatchToProps = dispatch =>
const propTypes = { const propTypes = {
children: PropTypes.func, children: PropTypes.func,
data: AllChallengeNode,
fetchUser: PropTypes.func.isRequired fetchUser: PropTypes.func.isRequired
}; };
@@ -79,13 +75,7 @@ class Layout extends PureComponent {
} }
} }
render() { render() {
const { const { children } = this.props;
children,
data: {
allChallengeNode: { edges },
allMarkdownRemark: { edges: mdEdges }
}
} = this.props;
return ( return (
<Fragment> <Fragment>
<Helmet <Helmet
@@ -103,12 +93,6 @@ class Layout extends PureComponent {
<div className='app-wrapper'> <div className='app-wrapper'>
<main>{children()}</main> <main>{children()}</main>
</div> </div>
<MapModal
introNodes={mdEdges.map(({ node }) => node)}
nodes={edges
.map(({ node }) => node)
.filter(({ isPrivate }) => !isPrivate)}
/>
</Fragment> </Fragment>
); );
} }
@@ -117,41 +101,3 @@ class Layout extends PureComponent {
Layout.propTypes = propTypes; Layout.propTypes = propTypes;
export default connect(mapStateToProps, mapDispatchToProps)(Layout); export default connect(mapStateToProps, mapDispatchToProps)(Layout);
export const query = graphql`
query LayoutQuery {
allChallengeNode(
filter: { isPrivate: { eq: false } }
sort: { fields: [superOrder, order, suborder] }
) {
edges {
node {
fields {
slug
blockName
}
id
block
title
isRequired
isPrivate
superBlock
dashedName
}
}
}
allMarkdownRemark(filter: { frontmatter: { block: { ne: null } } }) {
edges {
node {
frontmatter {
title
block
}
fields {
slug
}
}
}
}
}
`;

View File

@@ -23,5 +23,5 @@ main {
.app-wrapper { .app-wrapper {
margin-top: 38px; margin-top: 38px;
height: calc(100vh - 80px); height: calc(100vh - 38px);
} }

View File

@@ -5,4 +5,8 @@
align-items: center; align-items: center;
max-width: 960px; max-width: 960px;
margin: 0 auto; margin: 0 auto;
}
.index-page-wrapper p {
margin-bottom: 0.75rem;
} }

View File

@@ -1,15 +1,20 @@
/* global graphql */ /* global graphql */
/* eslint-disable max-len */
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Link from 'gatsby-link'; import Link from 'gatsby-link';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Button } from 'react-bootstrap';
import { ChallengeNode } from '../redux/propTypes'; import {
ChallengeNode,
AllChallengeNode,
AllMarkdownRemark
} from '../redux/propTypes';
import { toggleMapModal } from '../redux/app'; import { toggleMapModal } from '../redux/app';
import Spacer from '../components/util/Spacer'; import Spacer from '../components/util/Spacer';
import Map from '../components/Map';
import './index.css'; import './index.css';
@@ -20,14 +25,19 @@ const mapDispatchToProps = dispatch =>
const propTypes = { const propTypes = {
data: PropTypes.shape({ data: PropTypes.shape({
challengeNode: ChallengeNode challengeNode: ChallengeNode,
allChallengeNode: AllChallengeNode,
allMarkdownRemark: AllMarkdownRemark
}), }),
toggleMapModal: PropTypes.func.isRequired toggleMapModal: PropTypes.func.isRequired
}; };
const IndexPage = ({ const IndexPage = ({
data: { challengeNode: { title, fields: { slug, blockName } } }, data: {
toggleMapModal challengeNode: { fields: { slug } },
allChallengeNode: { edges },
allMarkdownRemark: { edges: mdEdges }
}
}) => ( }) => (
<div className='index-page-wrapper'> <div className='index-page-wrapper'>
<Helmet title='Welcome to learn.freeCodeCamp!' /> <Helmet title='Welcome to learn.freeCodeCamp!' />
@@ -35,27 +45,25 @@ const IndexPage = ({
<Spacer /> <Spacer />
<h2>Welcome to the freeCodeCamp curriculum</h2> <h2>Welcome to the freeCodeCamp curriculum</h2>
<p>We have thousands of coding lessons to help you improve your skills.</p> <p>We have thousands of coding lessons to help you improve your skills.</p>
<p>You can earn each certification by completing its 5 final projects.</p>
<p> <p>
You can earn verified certifications by completing each sections 6 And yes - all of this is 100% free, thanks to the thousands of campers who{' '}
required projects.
</p>
<p>
{'And yes - all of this is 100% free, thanks to the thousands of ' +
'campers who '}
<a href='https://donate.freecodecamp.org' target='_blank'> <a href='https://donate.freecodecamp.org' target='_blank'>
donate donate
</a>{' '} </a>{' '}
to our nonprofit. to our nonprofit.
</p> </p>
<h3>Not sure where to start?</h3>
<p> <p>
We recommend you start at the beginning{' '} If you are new to coding, we recommend you{' '}
<Link to={slug}>{`${blockName} -> ${title}`}</Link> <Link to={slug}>start at the beginning</Link>.
</p> </p>
<h3>Want to dive into our curriculum?</h3> <Spacer />
<Button block={true} bsSize='lg' bsStyle='primary' onClick={toggleMapModal}> <Map
Explore the curriculum introNodes={mdEdges.map(({ node }) => node)}
</Button> nodes={edges
.map(({ node }) => node)
.filter(({ isPrivate }) => !isPrivate)}
/>
</div> </div>
); );
@@ -67,10 +75,41 @@ export default connect(mapStateToProps, mapDispatchToProps)(IndexPage);
export const query = graphql` export const query = graphql`
query FirstChallenge { query FirstChallenge {
challengeNode(order: { eq: 0 }, suborder: { eq: 1 }) { challengeNode(order: { eq: 0 }, suborder: { eq: 1 }) {
title
fields { fields {
slug slug
blockName }
}
allChallengeNode(
filter: { isPrivate: { eq: false } }
sort: { fields: [superOrder, order, suborder] }
) {
edges {
node {
fields {
slug
blockName
}
id
block
title
isRequired
isPrivate
superBlock
dashedName
}
}
}
allMarkdownRemark(filter: { frontmatter: { block: { ne: null } } }) {
edges {
node {
frontmatter {
title
block
}
fields {
slug
}
}
} }
} }
} }

View File

@@ -1,18 +1,26 @@
/* global HOME_PATH */ /* global HOME_PATH */
import { of } from 'rxjs/observable/of'; import { of } from 'rxjs/observable/of';
import { merge } from 'rxjs/observable/merge';
import { ofType } from 'redux-observable'; import { ofType } from 'redux-observable';
import { types, fetchUserComplete, hardGoTo } from './'; import {
types,
fetchUserComplete,
fetchUserError,
noUserFound,
hardGoTo
} from './';
import { import {
switchMap, switchMap,
filter, filter,
map, map,
catchError, catchError,
defaultIfEmpty defaultIfEmpty,
mapTo
} from 'rxjs/operators'; } from 'rxjs/operators';
import { jwt } from '../cookieVaules'; import { jwt } from '../cookieVaules';
function fetchUserEpic(action$, _, { services }) { function fetchUserEpic(action$, _, { services }) {
return action$.pipe( const fetchUser = action$.pipe(
ofType(types.fetchUser), ofType(types.fetchUser),
filter(() => !!jwt), filter(() => !!jwt),
switchMap(() => { switchMap(() => {
@@ -25,14 +33,20 @@ function fetchUserEpic(action$, _, { services }) {
} }
return fetchUserComplete(response); return fetchUserComplete(response);
}), }),
defaultIfEmpty({ type: 'no-user' }), defaultIfEmpty(noUserFound()),
catchError(err => { catchError(() => {
console.log(err); return of(fetchUserError());
return of({ type: 'fetch-user-error' });
}) })
); );
}) })
); );
const isLoadingRequired = action$.pipe(
ofType(types.fetchUser),
filter(() => !jwt),
mapTo(noUserFound())
);
return merge(fetchUser, isLoadingRequired);
} }
export default fetchUserEpic; export default fetchUserEpic;

View File

@@ -14,6 +14,7 @@ export const types = createTypes(
'fetchUser', 'fetchUser',
'fetchUserComplete', 'fetchUserComplete',
'fetchUserError', 'fetchUserError',
'noUserFound',
'hardGoTo', 'hardGoTo',
'updateUserSignedIn', 'updateUserSignedIn',
'toggleMapModal' 'toggleMapModal'
@@ -23,6 +24,7 @@ export const types = createTypes(
const initialState = { const initialState = {
appUsername: '', appUsername: '',
showLoading: true,
isSignedIn: false, isSignedIn: false,
user: {}, user: {},
showMapModal: false showMapModal: false
@@ -30,7 +32,8 @@ const initialState = {
export const fetchUser = createAction(types.fetchUser); export const fetchUser = createAction(types.fetchUser);
export const fetchUserComplete = createAction(types.fetchUserComplete); export const fetchUserComplete = createAction(types.fetchUserComplete);
export const fecthUserError = createAction(types.fetchUserError); export const fetchUserError = createAction(types.fetchUserError);
export const noUserFound = createAction(types.noUserFound);
export const hardGoTo = createAction(types.hardGoTo); export const hardGoTo = createAction(types.hardGoTo);
@@ -41,6 +44,7 @@ export const updateUserSignedIn = createAction(types.updateUserSignedIn);
export const isMapModalOpenSelector = state => state[ns].showMapModal; export const isMapModalOpenSelector = state => state[ns].showMapModal;
export const isSignedInSelector = state => state[ns].isSignedIn; export const isSignedInSelector = state => state[ns].isSignedIn;
export const userSelector = state => state[ns].user || {}; export const userSelector = state => state[ns].user || {};
export const userStateLoadingSelector = state => state[ns].showLoading;
export const completedChallengesSelector = state => export const completedChallengesSelector = state =>
state[ns].user.completedChallenges || []; state[ns].user.completedChallenges || [];
export const currentChallengeIdSelector = state => export const currentChallengeIdSelector = state =>
@@ -55,8 +59,11 @@ export const reducer = handleActions(
...state, ...state,
appUsername: result, appUsername: result,
user: user[result], user: user[result],
showLoading: false,
isSignedIn: !!Object.keys(user).length isSignedIn: !!Object.keys(user).length
}), }),
[types.fetchUserError]: state => ({ ...state, showLoading: false }),
[types.noUserFound]: state => ({ ...state, showLoading: false }),
[types.toggleMapModal]: state => ({ [types.toggleMapModal]: state => ({
...state, ...state,
showMapModal: !state.showMapModal showMapModal: !state.showMapModal

View File

@@ -63,3 +63,11 @@ export const AllChallengeNode = PropTypes.shape({
}) })
) )
}); });
export const AllMarkdownRemark = PropTypes.shape({
edges: PropTypes.arrayOf(
PropTypes.shape({
node: MarkdownRemark
})
)
});

View File

@@ -119,7 +119,6 @@ export class BackEnd extends PureComponent {
const blockNameTitle = `${blockName} - ${title}`; const blockNameTitle = `${blockName} - ${title}`;
return ( return (
<Row> <Row>
<ProjectToolPanel />
<Col xs={6} xsOffset={3}> <Col xs={6} xsOffset={3}>
<Spacer /> <Spacer />
<div> <div>
@@ -134,6 +133,7 @@ export class BackEnd extends PureComponent {
options={options} options={options}
submit={executeChallenge} submit={executeChallenge}
/> />
<ProjectToolPanel />
</div> </div>
<div> <div>
<br /> <br />

View File

@@ -10,7 +10,6 @@ import { ReflexContainer, ReflexSplitter, ReflexElement } from 'react-reflex';
import Editor from './Editor'; import Editor from './Editor';
import Preview from '../components/Preview'; import Preview from '../components/Preview';
import SidePanel from '../components/Side-Panel'; import SidePanel from '../components/Side-Panel';
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';
@@ -32,8 +31,6 @@ import {
} from '../redux'; } from '../redux';
import './classic.css'; import './classic.css';
import ToolPanel from '../components/Tool-Panel';
import Spacer from '../../../components/util/Spacer';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
challengeFilesSelector, challengeFilesSelector,
@@ -157,7 +154,6 @@ class ShowClassic extends PureComponent {
} }
render() { render() {
// console.log(this.state)
const { const {
data: { data: {
challengeNode: { challengeNode: {
@@ -169,7 +165,6 @@ class ShowClassic extends PureComponent {
} }
}, },
files, files,
tests,
output output
} = this.props; } = this.props;
const editors = Object.keys(files) const editors = Object.keys(files)
@@ -202,9 +197,7 @@ class ShowClassic extends PureComponent {
<Output <Output
defaultOutput={` defaultOutput={`
/** /**
* Your output will go here. * Your test output will go here.
* Any console.log() statements
* will appear in here as well.
*/ */
`} `}
output={output} output={output}
@@ -220,12 +213,12 @@ class ShowClassic extends PureComponent {
return ( return (
<Fragment> <Fragment>
<Helmet title={`${blockNameTitle} | Learn freeCodeCamp`} /> <Helmet title={`${blockNameTitle} | Learn freeCodeCamp`} />
<ToolPanel guideUrl={guideUrl} />
<ReflexContainer orientation='vertical'> <ReflexContainer orientation='vertical'>
<ReflexElement flex={1} {...this.resizeProps}> <ReflexElement flex={1} {...this.resizeProps}>
<SidePanel <SidePanel
className='full-height' className='full-height'
description={description} description={description}
guideUrl={guideUrl}
title={blockNameTitle} title={blockNameTitle}
/> />
</ReflexElement> </ReflexElement>
@@ -234,16 +227,14 @@ class ShowClassic extends PureComponent {
{editors} {editors}
</ReflexElement> </ReflexElement>
<ReflexSplitter propagate={true} {...this.resizeProps} /> <ReflexSplitter propagate={true} {...this.resizeProps} />
<ReflexElement flex={0.5} {...this.resizeProps}> {showPreview ? (
{showPreview ? ( <ReflexElement flex={0.7} {...this.resizeProps}>
<Preview <Preview
className='full-height' className='full-height'
disableIframe={this.state.resizing} disableIframe={this.state.resizing}
/> />
) : null} </ReflexElement>
<Spacer /> ) : null}
<TestSuite tests={tests} />
</ReflexElement>
</ReflexContainer> </ReflexContainer>
<CompletionModal /> <CompletionModal />

View File

@@ -6,11 +6,16 @@ import { bindActionCreators } from 'redux';
import ChallengeTitle from './Challenge-Title'; import ChallengeTitle from './Challenge-Title';
import ChallengeDescription from './Challenge-Description'; import ChallengeDescription from './Challenge-Description';
import ToolPanel from './Tool-Panel';
import TestSuite from './Test-Suite';
import Spacer from '../../../components/util/Spacer'; import Spacer from '../../../components/util/Spacer';
import { initConsole } from '../redux'; import { initConsole, challengeTestsSelector } from '../redux';
import { createSelector } from 'reselect';
const mapStateToProps = () => ({}); const mapStateToProps = createSelector(challengeTestsSelector, tests => ({
tests
}));
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
bindActionCreators( bindActionCreators(
@@ -22,7 +27,9 @@ const mapDispatchToProps = dispatch =>
const propTypes = { const propTypes = {
description: PropTypes.arrayOf(PropTypes.string), description: PropTypes.arrayOf(PropTypes.string),
guideUrl: PropTypes.string,
initConsole: PropTypes.func.isRequired, initConsole: PropTypes.func.isRequired,
tests: PropTypes.arrayOf(PropTypes.object),
title: PropTypes.string title: PropTypes.string
}; };
@@ -52,7 +59,7 @@ export class SidePanel extends PureComponent {
} }
render() { render() {
const { title, description } = this.props; const { title, description, guideUrl, tests } = this.props;
return ( return (
<div className='instructions-panel' role='complementary'> <div className='instructions-panel' role='complementary'>
<div ref={this.bindTopDiv} /> <div ref={this.bindTopDiv} />
@@ -61,7 +68,9 @@ export class SidePanel extends PureComponent {
<ChallengeTitle>{title}</ChallengeTitle> <ChallengeTitle>{title}</ChallengeTitle>
<ChallengeDescription description={description} /> <ChallengeDescription description={description} />
</div> </div>
<Spacer /> <hr />
<ToolPanel guideUrl={guideUrl} />
<TestSuite tests={tests} />
</div> </div>
); );
} }

View File

@@ -2,7 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import GreenPass from './icons/GreenPass'; import GreenPass from './icons/GreenPass';
import RedFail from './icons/RedFail'; import Fail from './icons/Fail';
import Initial from './icons/Initial';
import './test-suite.css'; import './test-suite.css';
@@ -28,6 +29,8 @@ function TestSuite({ tests }) {
return ( return (
<div className='challenge-test-suite'> <div className='challenge-test-suite'>
{tests.map(({ err, pass = false, text = '' }, index) => { {tests.map(({ err, pass = false, text = '' }, index) => {
const isInitial = !pass && !err;
const statusIcon = pass && !err ? <GreenPass /> : <Fail />;
return ( return (
<div <div
aria-label={getAccessibleText(err, pass, text)} aria-label={getAccessibleText(err, pass, text)}
@@ -36,7 +39,7 @@ function TestSuite({ tests }) {
tabIndex='0' tabIndex='0'
> >
<div className='test-status-icon'> <div className='test-status-icon'>
{pass ? <GreenPass /> : <RedFail />} {isInitial ? <Initial /> : statusIcon}
</div> </div>
<div <div
aria-hidden='true' aria-hidden='true'

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux'; import { bindActionCreators } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@@ -7,7 +7,6 @@ import { Button } from 'react-bootstrap';
import './tool-panel.css'; import './tool-panel.css';
import { openModal, executeChallenge } from '../redux'; import { openModal, executeChallenge } from '../redux';
import { toggleMapModal } from '../../../redux/app';
const mapStateToProps = () => ({}); const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
@@ -15,8 +14,7 @@ const mapDispatchToProps = dispatch =>
{ {
executeChallenge, executeChallenge,
openHelpModal: () => openModal('help'), openHelpModal: () => openModal('help'),
openResetModal: () => openModal('reset'), openResetModal: () => openModal('reset')
toggleMapModal
}, },
dispatch dispatch
); );
@@ -25,37 +23,29 @@ const propTypes = {
executeChallenge: PropTypes.func.isRequired, executeChallenge: PropTypes.func.isRequired,
guideUrl: PropTypes.string, guideUrl: PropTypes.string,
openHelpModal: PropTypes.func.isRequired, openHelpModal: PropTypes.func.isRequired,
openResetModal: PropTypes.func.isRequired, openResetModal: PropTypes.func.isRequired
toggleMapModal: PropTypes.func.isRequired
}; };
function ToolPanel({ function ToolPanel({
executeChallenge, executeChallenge,
openHelpModal, openHelpModal,
openResetModal, openResetModal,
toggleMapModal,
guideUrl guideUrl
}) { }) {
return ( return (
<div className='tool-panel'> <Fragment>
<div id='left-tool-panel'> <div className='tool-panel-group'>
<Button <Button block={true} bsStyle='primary' onClick={executeChallenge}>
bsStyle='primary'
className='btn-primary-invert'
onClick={toggleMapModal}
>
View the Curriculum
</Button>
</div>
<div id='centre-tool-panel'>
<Button bsStyle='primary' onClick={executeChallenge}>
Run the Tests Run the Tests
</Button> </Button>
<Button bsStyle='default' onClick={openResetModal}> <Button
block={true}
bsStyle='primary'
className='btn-primary-invert'
onClick={openResetModal}
>
Reset All Code Reset All Code
</Button> </Button>
</div>
<div id='right-tool-panel'>
{guideUrl ? ( {guideUrl ? (
<Button <Button
block={true} block={true}
@@ -76,7 +66,7 @@ function ToolPanel({
Ask for help Ask for help
</Button> </Button>
</div> </div>
</div> </Fragment>
); );
} }

View File

@@ -1,7 +1,5 @@
import React from 'react'; import React from 'react';
const propTypes = {};
function RedFail() { function RedFail() {
return ( return (
<svg <svg
@@ -15,9 +13,9 @@ function RedFail() {
<circle <circle
cx='100' cx='100'
cy='99' cy='99'
fill='#ff0000' fill='#555'
r='95' r='95'
stroke='#ff0000' stroke='#555'
strokeDasharray='null' strokeDasharray='null'
strokeLinecap='null' strokeLinecap='null'
strokeLinejoin='null' strokeLinejoin='null'
@@ -52,6 +50,5 @@ function RedFail() {
} }
RedFail.displayName = 'RedFail'; RedFail.displayName = 'RedFail';
RedFail.propTypes = propTypes;
export default RedFail; export default RedFail;

View File

@@ -1,7 +1,5 @@
import React from 'react'; import React from 'react';
const propTypes = {};
function GreenPass(props) { function GreenPass(props) {
return ( return (
<svg <svg
@@ -53,6 +51,5 @@ function GreenPass(props) {
} }
GreenPass.displayName = 'GreenPass'; GreenPass.displayName = 'GreenPass';
GreenPass.propTypes = propTypes;
export default GreenPass; export default GreenPass;

View File

@@ -0,0 +1,55 @@
import React from 'react';
function Initial(props) {
return (
<svg
height='50'
viewBox='0 0 200 200'
width='50'
xmlns='http://www.w3.org/2000/svg'
{...props}
>
<g>
<title>Initial</title>
<circle
cx='100'
cy='99'
fill='#555'
r='95'
stroke='#555'
strokeDasharray='null'
strokeLinecap='null'
strokeLinejoin='null'
/>
<svg
height='200'
viewBox='-13 -12 50 50'
width='200'
xmlns='http://www.w3.org/2000/svg'
>
<path
d={
'M8 1c0-.552.448-1 1-1h6c.553 0 1 .448 1 1s-.447 1-1 1h-6c-' +
'.552 0-1-.448-1-1zm13 20.554c0 1.284-1.023 2.446-2.424 ' +
'2.446h-13.153c-1.4 0-2.423-1.162-2.423-2.445 0-.35.076-.709.' +
'242-1.057l3.743-7.856c1.04-2.186 2.015-4.581 2.015-7.007v-1.' +
'635h2l-.006 2c-.087 2.623-1.09 5.092-1.973 7h3.682l4.377 9h1.' +
'496c.309 0 .52-.342.377-.644l-3.743-7.854c-1.046-2.197-2.12-4' +
'.791-2.21-7.502v-2h2v1.635c0 2.426.975 4.82 2.016 7.006l3.743' +
' 7.856c.165.348.241.707.241 1.057zm-12-1.054c0-.829-.671-1.5-' +
'1.5-1.5s-1.5.671-1.5 1.5.671 1.5 1.5 1.5 1.5-.671 1.5-1.5zm2-' +
'3.5c0-.553-.448-1-1-1-.553 0-1 .447-1 1s.447 1 1 1c.552 0 1-.' +
'447 1-1zm3 3c0-.552-.448-1-1-1s-1 .448-1 1 .448 1 1 1 1-.448 ' +
'1-1z'
}
fill='#fff'
/>
</svg>
</g>
</svg>
);
}
Initial.displayName = 'Initial';
export default Initial;

View File

@@ -6,7 +6,7 @@
} }
.challenge-preview, .challenge-preview-frame { .challenge-preview, .challenge-preview-frame {
height: calc(60vh); height: calc(100vh - 40px);
width: 100%; width: 100%;
padding: 0; padding: 0;
margin: 0; margin: 0;

View File

@@ -2,6 +2,7 @@
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
flex-direction: column; flex-direction: column;
margin-top: 15px;
} }
.test-result { .test-result {

View File

@@ -1,22 +1,8 @@
.tool-panel { .tool-panel-group button, .tool-panel-group a {
height: 40px; font-size: 1.1rem;
display: flex;
justify-content: space-around;
align-items: center;
background-color: #99C199;
} }
#centre-tool-panel { .project-tool-panel {
width: 300px; width: 100%;
} padding: 0 15px;
#right-tool-panel {
display: flex;
justify-content: center;
align-items: center;
}
#right-tool-panel a, #right-tool-panel .btn-block {
margin: 0;
} }

View File

@@ -24,6 +24,7 @@ import {
import { frontEndProject } from '../../../../utils/challengeTypes'; import { frontEndProject } from '../../../../utils/challengeTypes';
import './project.css'; import './project.css';
import Spacer from '../../../components/util/Spacer';
const mapStateToProps = () => ({}); const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch => const mapDispatchToProps = dispatch =>
@@ -106,7 +107,6 @@ export class Project extends PureComponent {
return ( return (
<Fragment> <Fragment>
<Helmet title={`${blockNameTitle} | Learn freeCodeCamp}`} /> <Helmet title={`${blockNameTitle} | Learn freeCodeCamp}`} />
<ToolPanel />
<div className='project-show-wrapper'> <div className='project-show-wrapper'>
<SidePanel <SidePanel
className='full-height' className='full-height'
@@ -119,6 +119,8 @@ export class Project extends PureComponent {
openModal={openCompletionModal} openModal={openCompletionModal}
updateProjectForm={updateProjectFormValues} updateProjectForm={updateProjectFormValues}
/> />
<ToolPanel />
<Spacer />
</div> </div>
<CompletionModal /> <CompletionModal />
<HelpModal /> <HelpModal />

View File

@@ -27,40 +27,28 @@ const propTypes = {
export class ToolPanel extends PureComponent { export class ToolPanel extends PureComponent {
render() { render() {
const { guideUrl, openHelpModal, toggleMapModal } = this.props; const { guideUrl, openHelpModal } = this.props;
return ( return (
<div className='tool-panel'> <div className='tool-panel-group project-tool-panel'>
<div id='left-tool-panel sub-panel'> {guideUrl && (
<Button
bsStyle='primary'
className='btn-primary-invert'
onClick={toggleMapModal}
>
View the Curriculum
</Button>
</div>
<div id='centre-tool-panel sub-panel' />
<div id='right-tool-panel sub-panel'>
{guideUrl && (
<Button
block={true}
bsStyle='primary'
className='btn-primary-invert'
href={guideUrl}
target='_blank'
>
Get a hint
</Button>
)}
<Button <Button
block={true} block={true}
bsStyle='primary' bsStyle='primary'
className='btn-primary-invert' className='btn-primary-invert'
onClick={openHelpModal} href={guideUrl}
target='_blank'
> >
Ask for help Get a hint
</Button> </Button>
</div> )}
<Button
block={true}
bsStyle='primary'
className='btn-primary-invert'
onClick={openHelpModal}
>
Ask for help
</Button>
</div> </div>
); );
} }

View File

@@ -1,29 +1,21 @@
/* global graphql */ /* global graphql */
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux'; import Link, { navigateTo } from 'gatsby-link';
import { connect } from 'react-redux';
import Link from 'gatsby-link';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import { Button, ListGroup, ListGroupItem } from 'react-bootstrap'; import { Button, ListGroup, ListGroupItem } from 'react-bootstrap';
import FullWidthRow from '../../components/util/FullWidthRow'; import FullWidthRow from '../../components/util/FullWidthRow';
import ButtonSpacer from '../../components/util/ButtonSpacer'; import ButtonSpacer from '../../components/util/ButtonSpacer';
import { toggleMapModal } from '../../redux/app';
import { MarkdownRemark, AllChallengeNode } from '../../redux/propTypes'; import { MarkdownRemark, AllChallengeNode } from '../../redux/propTypes';
import './intro.css'; import './intro.css';
const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch =>
bindActionCreators({ toggleMapModal }, dispatch);
const propTypes = { const propTypes = {
data: PropTypes.shape({ data: PropTypes.shape({
markdownRemark: MarkdownRemark, markdownRemark: MarkdownRemark,
allChallengeNode: AllChallengeNode allChallengeNode: AllChallengeNode
}), })
toggleMapModal: PropTypes.func.isRequired
}; };
function renderMenuItems({ edges = [] }) { function renderMenuItems({ edges = [] }) {
@@ -34,10 +26,11 @@ function renderMenuItems({ edges = [] }) {
)); ));
} }
function IntroductionPage({ function handleCurriculumClick() {
data: { markdownRemark, allChallengeNode }, return navigateTo('/');
toggleMapModal }
}) {
function IntroductionPage({ data: { markdownRemark, allChallengeNode } }) {
const { html, frontmatter: { block } } = markdownRemark; const { html, frontmatter: { block } } = markdownRemark;
const firstLesson = allChallengeNode && allChallengeNode.edges[0].node; const firstLesson = allChallengeNode && allChallengeNode.edges[0].node;
const firstLessonPath = firstLesson const firstLessonPath = firstLesson
@@ -63,7 +56,7 @@ function IntroductionPage({
block={true} block={true}
bsSize='lg' bsSize='lg'
className='btn-primary-invert' className='btn-primary-invert'
onClick={toggleMapModal} onClick={handleCurriculumClick}
> >
View the curriculum View the curriculum
</Button> </Button>
@@ -83,7 +76,7 @@ function IntroductionPage({
IntroductionPage.displayName = 'IntroductionPage'; IntroductionPage.displayName = 'IntroductionPage';
IntroductionPage.propTypes = propTypes; IntroductionPage.propTypes = propTypes;
export default connect(mapStateToProps, mapDispatchToProps)(IntroductionPage); export default IntroductionPage;
export const query = graphql` export const query = graphql`
query IntroPageBySlug($slug: String!, $block: String!) { query IntroPageBySlug($slug: String!, $block: String!) {

View File

@@ -1914,13 +1914,6 @@ callsites@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
dependencies:
camelcase "^2.0.0"
map-obj "^1.0.0"
camelcase@4.1.0, camelcase@^4.0.0, camelcase@^4.1.0: camelcase@4.1.0, camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0" version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -1929,10 +1922,6 @@ camelcase@^1.0.2:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
camelcase@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
camelcase@^3.0.0: camelcase@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
@@ -2137,7 +2126,7 @@ class-utils@^0.3.5:
isobject "^3.0.0" isobject "^3.0.0"
static-extend "^0.1.1" static-extend "^0.1.1"
classnames@^2.2.5: classnames@^2.2.3, classnames@^2.2.5:
version "2.2.5" version "2.2.5"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
@@ -3011,12 +3000,6 @@ dom-serializer@0, dom-serializer@~0.1.0:
domelementtype "~1.1.1" domelementtype "~1.1.1"
entities "~1.1.1" entities "~1.1.1"
dom-urls@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/dom-urls/-/dom-urls-1.1.0.tgz#001ddf81628cd1e706125c7176f53ccec55d918e"
dependencies:
urijs "^1.16.1"
dom-walk@^0.1.0: dom-walk@^0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018"
@@ -3335,7 +3318,7 @@ es6-promise@3.2.1:
version "3.2.1" version "3.2.1"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.2.1.tgz#ec56233868032909207170c39448e24449dd1fc4" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.2.1.tgz#ec56233868032909207170c39448e24449dd1fc4"
es6-promise@^4.0.2, es6-promise@^4.0.5, es6-promise@^4.1.0: es6-promise@^4.0.2, es6-promise@^4.1.0:
version "4.2.4" version "4.2.4"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29"
@@ -4208,13 +4191,6 @@ gatsby-plugin-google-fonts@^0.0.4:
version "0.0.4" version "0.0.4"
resolved "https://registry.yarnpkg.com/gatsby-plugin-google-fonts/-/gatsby-plugin-google-fonts-0.0.4.tgz#dc1402a71f27c3ae6caee10777d10adadf74bd7c" resolved "https://registry.yarnpkg.com/gatsby-plugin-google-fonts/-/gatsby-plugin-google-fonts-0.0.4.tgz#dc1402a71f27c3ae6caee10777d10adadf74bd7c"
gatsby-plugin-offline@^1.0.15:
version "1.0.15"
resolved "https://registry.yarnpkg.com/gatsby-plugin-offline/-/gatsby-plugin-offline-1.0.15.tgz#26af0e1d1c8ed0c31f92bc6a75484b583550ef5b"
dependencies:
babel-runtime "^6.26.0"
sw-precache "^5.0.0"
gatsby-plugin-react-helmet@^2.0.8: gatsby-plugin-react-helmet@^2.0.8:
version "2.0.8" version "2.0.8"
resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-2.0.8.tgz#26928bfd38f6828f479d76393839523ddf85b005" resolved "https://registry.yarnpkg.com/gatsby-plugin-react-helmet/-/gatsby-plugin-react-helmet-2.0.8.tgz#26928bfd38f6828f479d76393839523ddf85b005"
@@ -4467,10 +4443,6 @@ get-port@^3.0.0:
version "3.2.0" version "3.2.0"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc" resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
get-stdin@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
get-stream@^3.0.0: get-stream@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
@@ -5151,12 +5123,6 @@ imurmurhash@^0.1.4:
version "0.1.4" version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
indent-string@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
dependencies:
repeating "^2.0.0"
indexes-of@^1.0.1: indexes-of@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
@@ -6362,6 +6328,10 @@ loader-utils@^1.0.2, loader-utils@^1.1.0:
emojis-list "^2.0.0" emojis-list "^2.0.0"
json5 "^0.5.0" json5 "^0.5.0"
loaders.css@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/loaders.css/-/loaders.css-0.1.2.tgz#3a9fb43726c73334a38142af9d0629019b658743"
locate-path@^2.0.0: locate-path@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -6401,7 +6371,7 @@ lodash.clonedeep@^4.5.0:
version "4.5.0" version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
lodash.defaults@^4.0.1, lodash.defaults@^4.2.0: lodash.defaults@^4.0.1:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
@@ -6497,7 +6467,7 @@ lodash.sortby@^4.7.0:
version "4.7.0" version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
lodash.template@^4.2.4, lodash.template@^4.4.0: lodash.template@^4.2.4:
version "4.4.0" version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0"
dependencies: dependencies:
@@ -6566,7 +6536,7 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3
dependencies: dependencies:
js-tokens "^3.0.0" js-tokens "^3.0.0"
loud-rejection@^1.0.0, loud-rejection@^1.2.0: loud-rejection@^1.2.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f"
dependencies: dependencies:
@@ -6623,10 +6593,6 @@ map-cache@^0.2.0, map-cache@^0.2.2:
version "0.2.2" version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
map-visit@^1.0.0: map-visit@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
@@ -6754,21 +6720,6 @@ memory-fs@~0.4.1:
errno "^0.1.3" errno "^0.1.3"
readable-stream "^2.0.1" readable-stream "^2.0.1"
meow@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
dependencies:
camelcase-keys "^2.0.0"
decamelize "^1.1.2"
loud-rejection "^1.0.0"
map-obj "^1.0.1"
minimist "^1.1.3"
normalize-package-data "^2.3.4"
object-assign "^4.0.1"
read-pkg-up "^1.0.1"
redent "^1.0.0"
trim-newlines "^1.0.0"
merge-descriptors@1.0.1: merge-descriptors@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
@@ -6905,7 +6856,7 @@ minimist@0.0.8:
version "0.0.8" version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: minimist@^1.1.1, minimist@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -7199,7 +7150,7 @@ nopt@^4.0.1:
abbrev "1" abbrev "1"
osenv "^0.1.4" osenv "^0.1.4"
normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: normalize-package-data@^2.3.2:
version "2.4.0" version "2.4.0"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f"
dependencies: dependencies:
@@ -7711,7 +7662,7 @@ path-to-regexp@0.1.7:
version "0.1.7" version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
path-to-regexp@^1.0.1, path-to-regexp@^1.7.0: path-to-regexp@^1.7.0:
version "1.7.0" version "1.7.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.7.0.tgz#59fde0f435badacba103a84e9d3bc64e96b9937d"
dependencies: dependencies:
@@ -8860,6 +8811,15 @@ react-side-effect@^1.1.0:
exenv "^1.2.1" exenv "^1.2.1"
shallowequal "^1.0.1" shallowequal "^1.0.1"
react-spinkit@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/react-spinkit/-/react-spinkit-3.0.0.tgz#31fdaf4e18177766c57d1b1f3330290f8492a85a"
dependencies:
classnames "^2.2.3"
loaders.css "^0.1.2"
object-assign "^4.1.0"
prop-types "^15.5.8"
react-test-renderer@^16.0.0-0, react-test-renderer@^16.3.1: react-test-renderer@^16.0.0-0, react-test-renderer@^16.3.1:
version "16.3.1" version "16.3.1"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.1.tgz#d9257936d8535bd40f57f3d5a84e7b0452fb17f2" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.3.1.tgz#d9257936d8535bd40f57f3d5a84e7b0452fb17f2"
@@ -9036,13 +8996,6 @@ redbox-react@^1.3.6:
prop-types "^15.5.4" prop-types "^15.5.4"
sourcemapped-stacktrace "^1.1.6" sourcemapped-stacktrace "^1.1.6"
redent@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
dependencies:
indent-string "^2.1.0"
strip-indent "^1.0.1"
reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7: reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
@@ -9817,10 +9770,6 @@ serve@^6.4.0:
path-type "3.0.0" path-type "3.0.0"
send "0.16.2" send "0.16.2"
serviceworker-cache-polyfill@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/serviceworker-cache-polyfill/-/serviceworker-cache-polyfill-4.0.0.tgz#de19ee73bef21ab3c0740a37b33db62464babdeb"
set-blocking@^1.0.0: set-blocking@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-1.0.0.tgz#cd5e5d938048df1ac92dfe92e1f16add656f5ec5" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-1.0.0.tgz#cd5e5d938048df1ac92dfe92e1f16add656f5ec5"
@@ -10421,12 +10370,6 @@ strip-eof@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
strip-indent@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
dependencies:
get-stdin "^4.0.1"
strip-json-comments@~2.0.1: strip-json-comments@~2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
@@ -10486,28 +10429,6 @@ svgo@^0.7.0:
sax "~1.2.1" sax "~1.2.1"
whet.extend "~0.9.9" whet.extend "~0.9.9"
sw-precache@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/sw-precache/-/sw-precache-5.2.1.tgz#06134f319eec68f3b9583ce9a7036b1c119f7179"
dependencies:
dom-urls "^1.1.0"
es6-promise "^4.0.5"
glob "^7.1.1"
lodash.defaults "^4.2.0"
lodash.template "^4.4.0"
meow "^3.7.0"
mkdirp "^0.5.1"
pretty-bytes "^4.0.2"
sw-toolbox "^3.4.0"
update-notifier "^2.3.0"
sw-toolbox@^3.4.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/sw-toolbox/-/sw-toolbox-3.6.0.tgz#26df1d1c70348658e4dea2884319149b7b3183b5"
dependencies:
path-to-regexp "^1.0.1"
serviceworker-cache-polyfill "^4.0.0"
symbol-observable@1.0.1: symbol-observable@1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
@@ -10753,10 +10674,6 @@ trim-lines@^1.0.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.0.tgz#9926d03ede13ba18f7d42222631fb04c79ff26fe" resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-1.1.0.tgz#9926d03ede13ba18f7d42222631fb04c79ff26fe"
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
trim-right@^1.0.1: trim-right@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
@@ -11074,10 +10991,6 @@ uri-js@^3.0.2:
dependencies: dependencies:
punycode "^2.1.0" punycode "^2.1.0"
urijs@^1.16.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.1.tgz#5b0ff530c0cbde8386f6342235ba5ca6e995d25a"
urix@^0.1.0: urix@^0.1.0:
version "0.1.0" version "0.1.0"
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
@@ -11352,6 +11265,10 @@ webpack-md5-hash@0.0.5:
dependencies: dependencies:
md5 "^2.0.0" md5 "^2.0.0"
webpack-remove-serviceworker-plugin@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/webpack-remove-serviceworker-plugin/-/webpack-remove-serviceworker-plugin-1.0.0.tgz#63a7604da9a7fd9bae8f9eef87d274a2470dcaa7"
webpack-sources@^0.1.0: webpack-sources@^0.1.0:
version "0.1.5" version "0.1.5"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.5.tgz#aa1f3abf0f0d74db7111c40e500b84f966640750" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-0.1.5.tgz#aa1f3abf0f0d74db7111c40e500b84f966640750"