diff --git a/client/less/challenge.less b/client/less/challenge.less index 5ccb4ca610..7aeca53da8 100644 --- a/client/less/challenge.less +++ b/client/less/challenge.less @@ -51,9 +51,9 @@ } } -#testSuite { +.challenge-test-suite { margin-top: 10px; - > div >.row { + & .row { margin: 0!important; } } diff --git a/client/less/main.less b/client/less/main.less index fdda85a0a5..49ee08fe87 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -1,4 +1,4 @@ -@import "lib/bootstrap/bootstrap"; + @import "lib/bootstrap/bootstrap"; @import "lib/bootstrap-social/bootstrap-social"; @import "lib/ionicons/ionicons"; @import "lib/animate.min.less"; @@ -682,21 +682,11 @@ form.update-email .btn{ padding-bottom: 117%; } -#directions { - text-align: left; - font-size: 15px; -} - .graph-rect { fill: #ddd !important } - -/** - * Challenge styling - */ - -form.code span { +.CodeMirror span { font-size: 18px; font-family: "Ubuntu Mono"; padding-bottom: 0px; @@ -713,7 +703,7 @@ form.code span { font-family: "Ubuntu Mono"; } -#mainEditorPanel { +.challenges-editor { height: 100%; width: 99%; } @@ -723,10 +713,6 @@ form.code span { overflow-y: auto; } -#mainEditorPanel .panel-body { - padding-bottom: 0px; -} - div.CodeMirror-scroll { padding-bottom: 30px; } @@ -742,6 +728,11 @@ div.CodeMirror-scroll { min-height: 650px; } +.challenge-log .CodeMirror { + height: 100%; + width: 100%; +} + .btn { font-weight: 400; white-space: normal; diff --git a/common/app/App.jsx b/common/app/App.jsx index 0afb196a5b..130ac7f50f 100644 --- a/common/app/App.jsx +++ b/common/app/App.jsx @@ -5,8 +5,13 @@ import { compose } from 'redux'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; -import { fetchUser } from './redux/actions'; +import { + fetchUser, + updateWindowHeight, + updateNavHeight +} from './redux/actions'; import contain from './utils/professor-x'; +import getWindowHeight from './utils/get-window-height'; import Nav from './components/Nav'; @@ -43,7 +48,9 @@ export class FreeCodeCamp extends React.Component { username: PropTypes.string, points: PropTypes.number, picture: PropTypes.string, - toast: PropTypes.object + toast: PropTypes.object, + updateNavHeight: PropTypes.func, + updateWindowHeight: PropTypes.func }; componentWillReceiveProps({ toast: nextToast = {} }) { @@ -60,9 +67,13 @@ export class FreeCodeCamp extends React.Component { } } + componentDidMount() { + this.props.updateWindowHeight(getWindowHeight()); + } + render() { - const { username, points, picture } = this.props; - const navProps = { username, points, picture }; + const { username, points, picture, updateNavHeight } = this.props; + const navProps = { username, points, picture, updateNavHeight }; return (
//
will tell JavaScript to ignore the remainder of the text on the current line:',
+ '// This is an in-line comment.', + 'You can make a multi-line comment beginning with
/*
and ending with */
:',
+ '/* This is a', + 'Best Practice
multi-line comment */
{ + if (descriptionRegex.test(line)) { + return ( + + ); + } + return ( + + ); + }); + } + + render() { + const { height } = this.props; + const style = { + overflowX: 'hidden', + overflowY: 'auto' + }; + if (height) { + style.height = height + 'px'; + } + return ( +++ ); + } +} + +export default connect(mapStateToProps)(SidePanel); diff --git a/common/app/routes/challenges/components/Test-Suite.jsx b/common/app/routes/challenges/components/Test-Suite.jsx new file mode 100644 index 0000000000..17e69353f0 --- /dev/null +++ b/common/app/routes/challenges/components/Test-Suite.jsx @@ -0,0 +1,68 @@ +import React, { PropTypes } from 'react'; +import PureComponent from 'react-pure-render/component'; + +import { Col, Row } from 'react-bootstrap'; + +/* eslint-disable max-len, quotes */ +const tests = [{ + err: null, + text: "assert((function(z){if(z.hasOwnProperty(\"name\") && z.name !== undefined && typeof z.name === \"string\"){return true;}else{return false;}})(myDog), 'message:+++ Build JavaScript Objects +
+
++
++ { this.renderDescription(description, this.descriptionRegex) } + + + +
++ myDog
should contain the propertyname
and it should be astring
.');" + }, { + err: "message", + text: "assert((function(z){if(z.hasOwnProperty(\"legs\") && z.legs !== undefined && typeof z.legs === \"number\"){return true;}else{return false;}})(myDog), 'message:myDog
should contain the propertylegs
and it should be anumber
.');" + }, { + err: "message", + text: "assert((function(z){if(z.hasOwnProperty(\"tails\") && z.tails !== undefined && typeof z.tails === \"number\"){return true;}else{return false;}})(myDog), 'message:myDog
should contain the propertytails
and it should be anumber
.');" + }, { + err: "message", + text: "assert((function(z){if(z.hasOwnProperty(\"friends\") && z.friends !== undefined && Array.isArray(z.friends)){return true;}else{return false;}})(myDog), 'message:myDog
should contain the propertyfriends
and it should be anarray
.');" + }, { + err: "message", + text: "assert((function(z){return Object.keys(z).length === 4;})(myDog), 'message:myDog
should only contain all the given properties.');" +}]; +/* eslint-enable max-len, quotes */ + +export default class extends PureComponent { + static displayName = 'TestSuite'; + static proptTypes = { + tests: PropTypes.arrayOf(PropTypes.object) + }; + + static defaultProps = { + tests: tests + }; + + renderTests(tests = []) { + return tests.map(({ err, text = '' }, index)=> { + var iconClass = err ? + 'ion-close-circled big-error-icon' : + 'ion-checkmark-circled big-success-icon'; + return ( ++
+ ); + }); + } + + render() { + const { tests } = this.props; + return ( ++ + + + + { this.renderTests(tests) } ++ ); + } +} diff --git a/common/app/routes/challenges/components/Tool-Panel.jsx b/common/app/routes/challenges/components/Tool-Panel.jsx new file mode 100644 index 0000000000..c8d2666fe7 --- /dev/null +++ b/common/app/routes/challenges/components/Tool-Panel.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { Button, ButtonGroup } from 'react-bootstrap'; +import PureComponent from 'react-pure-render/component'; + +export default class extends PureComponent { + static displayName = 'ToolPanel'; + + render() { + return ( ++ + ++ ); + } +} diff --git a/common/app/routes/challenges/components/step/Step.jsx b/common/app/routes/challenges/components/step/Step.jsx new file mode 100644 index 0000000000..a14b23a98b --- /dev/null +++ b/common/app/routes/challenges/components/step/Step.jsx @@ -0,0 +1,10 @@ +import React from 'react'; +import PureComponent from 'react-pure-render/component'; + +export default class extends PureComponent { + render() { + return ( + + ); + } +} diff --git a/common/app/routes/challenges/index.js b/common/app/routes/challenges/index.js new file mode 100644 index 0000000000..81b28ea225 --- /dev/null +++ b/common/app/routes/challenges/index.js @@ -0,0 +1,6 @@ +import Challenges from './components/Challenges.jsx'; + +export default { + path: 'challenges', + component: Challenges +}; diff --git a/common/app/routes/challenges/redux/actions.js b/common/app/routes/challenges/redux/actions.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/app/routes/challenges/redux/index.js b/common/app/routes/challenges/redux/index.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/app/routes/challenges/redux/types.js b/common/app/routes/challenges/redux/types.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/app/routes/index.js b/common/app/routes/index.js index 1dd1b704fd..d975f7e78b 100644 --- a/common/app/routes/index.js +++ b/common/app/routes/index.js @@ -1,5 +1,6 @@ import Jobs from './Jobs'; import Hikes from './Hikes'; +import Challenges from './challenges'; import NotFound from '../components/NotFound/index.jsx'; export default { @@ -7,6 +8,7 @@ export default { childRoutes: [ Jobs, Hikes, + Challenges, { path: '*', component: NotFound diff --git a/common/app/utils/get-window-height.js b/common/app/utils/get-window-height.js new file mode 100644 index 0000000000..08bb5e20bf --- /dev/null +++ b/common/app/utils/get-window-height.js @@ -0,0 +1,18 @@ +export default function getWindowHeight() { + try { + const win = typeof window !== 'undefined' ? + window : + null; + if (!win) { + return 0; + } + const docElement = win.document.documentElement; + const body = win.document.getElementsByTagName('body')[0]; + return win.innerHeight || + docElement.clientHeight || + body.clientHeight || + 0; + } catch (e) { + return 0; + } +} diff --git a/package.json b/package.json index ae695440a8..f4b3ec63a1 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "react-bootstrap": "~0.29.4", "react-dom": "^15.0.2", "react-motion": "~0.4.2", + "react-no-ssr": "^1.0.1", "react-pure-render": "^1.0.2", "react-redux": "^4.0.6", "react-router": "^2.0.0", diff --git a/server/boot/a-react.js b/server/boot/a-react.js index c9e895a5da..ff7411a09c 100644 --- a/server/boot/a-react.js +++ b/server/boot/a-react.js @@ -13,7 +13,8 @@ const log = debug('fcc:react-server'); // remove their individual controllers const routes = [ '/videos', - '/videos/*' + '/videos/*', + '/challenges' ]; const devRoutes = []; diff --git a/server/views/layout-react.jade b/server/views/layout-react.jade index e3cf910c33..f31f102c9a 100644 --- a/server/views/layout-react.jade +++ b/server/views/layout-react.jade @@ -6,8 +6,8 @@ html(lang='en') else title Free Code Camp include partials/react-stylesheets - body.top-and-bottom-margins(style='overflow: hidden') - .container - #fcc!= markup + body.no-top-and-bottom-margins(style='overflow: hidden') + #fcc!= markup script!= state + script(src=rev('/js', 'vendor-challenges.js')) script(src=rev('/js', 'bundle.js')) diff --git a/server/views/partials/react-stylesheets.jade b/server/views/partials/react-stylesheets.jade index 3152f1630b..d8ae245081 100644 --- a/server/views/partials/react-stylesheets.jade +++ b/server/views/partials/react-stylesheets.jade @@ -2,6 +2,10 @@ link(rel='stylesheet', type='text/css' href='/css/lato.css') link(rel='stylesheet', href='/bower_components/font-awesome/css/font-awesome.min.css') link(rel='stylesheet', href=rev('/css', 'main.css')) link(rel='stylesheet', href='/css/Vimeo.css') +link(rel='stylesheet', href='/bower_components/CodeMirror/lib/codemirror.css') +link(rel='stylesheet', href='/bower_components/CodeMirror/addon/lint/lint.css') +link(rel='stylesheet', href='/bower_components/CodeMirror/theme/monokai.css') +link(rel='stylesheet', href='/css/ubuntu.css') include meta meta(charset='utf-8') diff --git a/webpack.config.js b/webpack.config.js index 6069bb9e18..718e7b26d9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -31,6 +31,9 @@ module.exports = { } ] }, + externals: { + 'codemirror': 'CodeMirror' + }, plugins: [ new webpack.optimize.DedupePlugin(), new webpack.optimize.OccurenceOrderPlugin(true),+ + + + + +