diff --git a/client/less/challenge.less b/client/less/challenge.less index e5c79dc0c9..e69de29bb2 100644 --- a/client/less/challenge.less +++ b/client/less/challenge.less @@ -1,237 +0,0 @@ -.challenges-instructions-panel { - clear: both; - overflow-x: hidden; - overflow-y: auto; - padding-left: 5px; - padding-right: 5px; - margin-bottom: 10px; -} - -.challenge-step-description { - font-size: 1.5em; -} -.challenge-step-counter { - font-size: 20px; - line-height: 44px; -} - -.challenge-step-forward-leave { - transition: opacity .4s ease-in, transform .3s ease-in-out; - opacity: 1; - transform: translate(0, 0); -} - -.challenge-step-forward-leave-active { - opacity: 0; - transform: translate(-100%, 0); -} - -.challenge-step-forward-enter { - transition: opacity .4s ease-in, transform .3s ease-in-out; - opacity: 0; - transform: translate(100%, 0); -} - -.challenge-step-forward-enter-active { - opacity: 1; - transform: translate(0, 0); -} - -.challenge-step-backward-leave { - transition: opacity .4s ease-in, transform .3s ease-in-out; - opacity: 1; - transform: translate(0, 0); -} - -.challenge-step-backward-leave-active { - opacity: 0; - transform: translate(100%, 0); -} - -.challenge-step-backward-enter { - transition: opacity .4s ease-in, transform .3s ease-in-out; - opacity: 0; - transform: translate(-100%, 0); -} - -.challenge-step-backward-enter-active { - opacity: 1; - transform: translate(0, 0); -} - -.challenge-instructions-title { - margin-top: 0; - i { - margin-left: 5px; - line-height: 20px; - } -} - -.challenge-instructions { - margin-bottom: 5px; - h4 { - margin-bottom: 0; - } - blockquote { - font-size: 90%; - font-family: @font-family-monospace; - color: @code-color; - background-color: #fffbe5; - border-radius: @border-radius-base; - border: 1px solid @pre-border-color; - white-space: pre; - padding: 5px 10px; - margin-bottom: 10px; - margin-top: -5px; - overflow: auto; - } - dfn { - font-family: @font-family-monospace; - color: @code-color; - background-color: @code-bg; - border-radius: @border-radius-base; - padding: 1px 5px; - } - & a, #MDN-links a { - color: #31708f; - } - & a::after, #MDN-links a::after { - font-size: 70%; - font-family: FontAwesome; - content: " \f08e"; - } - ol { - font-size: 16px; - } -} - -.challenge-test-suite { - margin-top: 10px; - & .row { - margin: 0!important; - } -} - -.test-output { - font-size: 15px; - font-family: "Ubuntu Mono"; - margin-top: 8px; - line-height:20px; - word-wrap: break-word; -} - -.grayed-out-test-output { - color: @gray-light; -} - -.big-icon { - font-size: 30px; -} - -.error-icon { - color: @brand-danger; - top: 50%; -} - -.success-icon { - color: @brand-primary; -} - -.refresh-icon { - color: @icon-grey; -} - -.night { - .challenge-instructions blockquote { - background-color: #242424; - border-color: #515151; - color: #ABABAB - } - .challenge-instructions dfn { - background-color: #242424; - color: #02a902; - } - .CodeMirror { - background-color:#242424; - color:#ABABAB; - &-gutters { - background-color:#242424; - color:#ABABAB; - } - .cm-bracket, .cm-tag { - color:#5CAFD6; - } - .cm-property, .cm-string { - color:#B5753A; - } - .cm-keyword, .cm-attribute { - color:#9BBBDC; - } - } - .refresh-icon { - color: @icon-light-grey; - } -} - -.challenges-editor { - height: auto; - width: 100%; - overflow-y: auto; -} - -.challenges-preview { - clear: both; - overflow: hidden; -} - -@media only screen and (min-width: 1031px) { - .iframe-scroll { - z-index: 1; - } -} -@media only screen and (max-width: 1030px) { - .iframe-scroll { - height: auto; - overflow: auto; - } - .iphone-position { - display: none; - } -} - -iframe.iphone { - border: none; - @media(min-width: 1031px) { - width: 280px; - height: 497px; - position: absolute; - top: 75px; - right: 35px; - overflow-y: scroll; - } - @media(max-width: 1030px) { - width: 100%; - border-radius: 5px; - overflow-y: visible; - height: 500px; - } - @media (min-width: 1200px) and (max-width: 1250px){ - right: 22px; - } -} - -// To adjust right margin, negative values bring the image closer to the edge of the screen -.iphone-position { - position: absolute; - top: -45px; - z-index: -1; - right: -195px; - @media (min-width: 1200px) and (max-width: 1250px){ - right: -207px; - } -} - -// YouTube embed -.embed-responsive-item { - max-width: 100%; -} diff --git a/client/less/flexgrid.less b/client/less/flexgrid.less new file mode 100644 index 0000000000..de154c4482 --- /dev/null +++ b/client/less/flexgrid.less @@ -0,0 +1,117 @@ +.justify-mixin(start) { justify-content: flex-start } +.justify-mixin(end) { justify-content: flex-end } +.justify-mixin(center) { justify-content: center } +.justify-mixin(around) { justify-content: space-around } +.justify-mixin(between) { justify-content: between } +.justify-mixin(@_) {} + +.items-mixin(top) { align-items: flex-start; } +.items-mixin(bottom) { align-items: flex-end; } +.items-mixin(center) { align-items: center; } +.items-mixin(stretch) { align-items: stretch; } +.items-mixin(baseline) { align-items: baseline; } +.items-mixin(@_) {} + +.item-mixin(top) { align-self: flex-start; } +.item-mixin(bottom) { align-self: flex-end; } +.item-mixin(center) { align-self: center; } +.item-mixin(stretch) { align-self: stretch; } +.item-mixin(baseline) { align-self: baseline; } +.item-mixin(@_) {} + +.content-mixin(top) { align-content: flex-start; } +.content-mixin(bottom) { align-content: flex-end; } +.content-mixin(center) { align-content: center; } +.content-mixin(stretch) { align-content: stretch; } +.content-mixin(baseline) { align-content: baseline; } +.content-mixin(@_) {} + +.grid(@direction: row; @items: none; @justify: none; @content: none) { + display: flex; + flex-wrap: wrap; + flex-direction: @direction; + + .justify-mixin(@justify); + .items-mixin(@item); + .content-mixin(@content); +} + +.row(@items: none; @justify: none; @content: none) { + .grid(@direction: row, @items, @justify, @content); +} + +.column(@items: none; @justify: none; @content: none) { + .grid(@direction: column; @items; @justify; @content); +} + +.margin-mixin(@g) when not (@g = 0) { + margin: @g / 2; +} + +.cell(@i: 1; @item; @g: @grid-gutter-width; @cols: @grid-columns) { + flex-basis: %('calc(100% * %s - %s)', @i / @cols, @g); + min-width: 0; // FF adjustment for responsive images + .item-mixin(@item); + .margin-mixin(@g); +} + +.cell-offset(@i: 1; @g: @grid-gutter-width; @cols: @grid-columns) { + margin-left: e%('calc(100% * %s + %s)', @i / @cols, @g / 2) !important; +} + +.padding-mixin(@pad) when (@pad) { + padding-left: @pad; + padding-right: @pad; +} + +// center an element +.center(@value: 50%; @padding: 0) { + margin-left: auto; + margin-right: auto; + max-width: @value; + width: 100%; + + .padding-mixin(@padding); +} + +.between(@min; @max; @rules) { + // BS logic to do string building with conditions + .condition-wrapper(@new) { + .redefine-condition() { + @condition: @new; + } + } + .init-condition() { + .condition-wrapper('only screen'); + } + .init-condition(); + .add-min-media(@min) when (iskeyword(@min)) { + .redefine-condition(); + .condition-wrapper(%('%s and (min-width: @{screen-%s})', @condition, @min)); + } + + .add-max-media(@max) when (iskeyword(@max)) { + .redefine-condition(); + .condition-wrapper(%('%s and (max-width: @{screen-%s})', @condition, @max)); + } + .add-min-media(@min); + .add-max-media(@max); + + .add-query() { + .redefine-condition(); + @query: e(@condition); + @media @query { + @rules(); + } + } + + .add-query(); +} + +.below(@max, @rules) { + .between(@empty; @max; @rules); +} + +.above(@min, @rules) { + .between(@min; @empty; @rules); +} diff --git a/client/less/lib/bootstrap/variables.less b/client/less/lib/bootstrap/variables.less index 5539c048e6..4498534de0 100755 --- a/client/less/lib/bootstrap/variables.less +++ b/client/less/lib/bootstrap/variables.less @@ -1,6 +1,7 @@ // // Variables // -------------------------------------------------- +@empty: ~''; //== Colors @@ -20,8 +21,8 @@ @brand-warning: #f0ad4e; @brand-danger: #d9534f; -@icon-grey: #575757; -@icon-light-grey: #888888; +@icon-gray: #575757; +@icon-light-gray: #888888; //== Scaffolding // //## Settings for some of the most global styles. @@ -305,6 +306,10 @@ //** Deprecated `@screen-lg-desktop` as of v3.0.1 @screen-lg-desktop: @screen-lg-min; +// Large screen / wide desktop +@screen-xl: 1400px; +@screen-lg-min: @screen-lg; + // So media queries don't overlap when required, provide a maximum @screen-xs-max: (@screen-sm-min - 1); @screen-sm-max: (@screen-md-min - 1); @@ -345,6 +350,8 @@ //** For `@screen-lg-min` and up. @container-lg: @container-large-desktop; +@container-xl: (1340px + @grid-gutter-width); + //== Navbar // diff --git a/client/less/main.less b/client/less/main.less index d70dc76365..683ccae15e 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -1,8 +1,9 @@ -@import "lib/bootstrap/bootstrap"; -@import "lib/bootstrap-social/bootstrap-social"; -@import "lib/ionicons/ionicons"; -@import "lib/animate"; -@import "lib/bootstrap/variables"; +@import "./lib/bootstrap/bootstrap"; +@import "./lib/bootstrap-social/bootstrap-social"; +@import "./lib/ionicons/ionicons"; +@import "./lib/animate"; +@import "./lib/bootstrap/variables"; +@import "./flexgrid.less"; html,body,div,span,a,li,td,th { font-family: 'Lato', sans-serif; @@ -34,13 +35,6 @@ pre.wrappable { word-wrap: break-word; /* IE 5+ */ } -html { - position: relative; - min-height: 100%; - // hack to prevent horizontal overflow problem on showHTML view - overflow-x: hidden; -} - //input[type=checkbox] { // /* Double-sized Checkboxes */ // -ms-transform: scale(2); /* IE */ @@ -54,28 +48,14 @@ html { border-color: @brand-primary; } -body.full-screen-body-background { +.full-screen-body-background { background-color: @body-bg; } -body.top-and-bottom-margins { - padding-top: 80px; - margin-bottom: 60px; -} - -body.no-top-and-bottom-margins { +.no-top-and-bottom-margins { margin: 75px 20px 0px 20px; } -body.react-layout { - margin-top: 75px; - margin-bottom: 15px; - width: auto; - padding-left: 15px; - padding-right: 15px; - min-height: 650px; -} - h1, h2 { font-weight: 400; } @@ -165,9 +145,12 @@ h1, h2, h3, h4, h5, h6, p, li { } } +// defined in bootstrap +@navbar-total-height: @navbar-height + @navbar-margin-bottom; .nav-height { - height: 50px; border: none; + height: @navbar-height; + width: 100%; } .landing-icon { @@ -1241,11 +1224,14 @@ and (max-width : 400px) { } } - -@import "code-mirror.less"; -@import "challenge.less"; -@import "toastr.less"; -@import "map.less"; -@import "sk-wave.less"; -@import "classic-modal.less"; -@import "skeleton-shimmer.less"; +// surrounding downstream import with &{} +// creates locally scoped imports +// and prevents vaiables from overwriting each other +&{ @import "./code-mirror.less"; } +&{ @import "./challenge.less"; } +&{ @import "./toastr.less"; } +&{ @import "./map.less"; } +&{ @import "./sk-wave.less"; } +&{ @import "./classic-modal.less"; } +&{ @import "./skeleton-shimmer.less"; } +&{ @import "../../common/index.less"; } diff --git a/client/less/map.less b/client/less/map.less index a4b07d3b68..58bf701ae0 100644 --- a/client/less/map.less +++ b/client/less/map.less @@ -54,27 +54,10 @@ width: 100%; } -.map-fixed-header { - position: fixed; - background: white; - width: 100%; - padding-top: 5px; - z-index: 1; - left: 0; - top: 0; -} - -.drawer .map-fixed-header { - padding-top: 30px; - position: static; - margin-bottom: -100px; -} - .map-accordion { - width: 100%; - margin-top: 100px; max-width: 700px; overflow-y: auto; + width: 100%; .map-accordion-panel-title { padding-bottom: 0px; diff --git a/client/sagas/index.js b/client/sagas/index.js index 8fe5789425..004e8f75f0 100644 --- a/client/sagas/index.js +++ b/client/sagas/index.js @@ -7,7 +7,6 @@ import hardGoToSaga from './hard-go-to-saga.js'; import mouseTrapSaga from './mouse-trap-saga.js'; import nightModeSaga from './night-mode-saga.js'; import titleSaga from './title-saga.js'; -import windowSaga from './window-saga.js'; export default [ analyticsSaga, @@ -18,6 +17,5 @@ export default [ hardGoToSaga, mouseTrapSaga, nightModeSaga, - titleSaga, - windowSaga + titleSaga ]; diff --git a/client/sagas/window-saga.js b/client/sagas/window-saga.js deleted file mode 100644 index 0e3302186d..0000000000 --- a/client/sagas/window-saga.js +++ /dev/null @@ -1,35 +0,0 @@ -import { Observable } from 'rx'; -import types from '../../common/app/redux/types'; -import { updateWindowHeight } from '../../common/app/redux/actions'; - -const { initWindowHeight } = types; -function getWindowSize(document, window) { - const body = document.getElementsByTagName('body')[0]; - return window.innerHeight || - document.docElement.clientHeight || - body.clientHeight || - 0; -} - -function listenForResize(document, window) { - return Observable.fromEvent(window, 'resize') - .debounce(250) - .startWith({}) - .map(() => getWindowSize(document, window)); -} - -export default function windowSaga( - action$, - getState, - { isDev, document, window } -) { - return action$ - .filter(({ type }) => type === initWindowHeight) - .flatMap(() => { - if (isDev) { - return listenForResize(document, window); - } - return Observable.just(getWindowSize(document, window)); - }) - .map(updateWindowHeight); -} diff --git a/common/app/App.jsx b/common/app/App.jsx index 72e4f7790c..bb488ad925 100644 --- a/common/app/App.jsx +++ b/common/app/App.jsx @@ -1,12 +1,11 @@ import React, { PropTypes } from 'react'; -import { Button, Row } from 'react-bootstrap'; +import { Button } from 'react-bootstrap'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; +import ns from './ns.json'; import { fetchUser, - initWindowHeight, - updateNavHeight, updateAppLang, trackEvent, loadCurrentChallenge, @@ -21,15 +20,13 @@ import Toasts from './toasts/Toasts.jsx'; import { userSelector } from './redux/selectors'; const mapDispatchToProps = { - initWindowHeight, - updateNavHeight, + closeDropdown, fetchUser, - submitChallenge, - updateAppLang, - trackEvent, loadCurrentChallenge, openDropdown, - closeDropdown + submitChallenge, + trackEvent, + updateAppLang }; const mapStateToProps = createSelector( @@ -58,7 +55,6 @@ const propTypes = { children: PropTypes.node, closeDropdown: PropTypes.func.isRequired, fetchUser: PropTypes.func, - initWindowHeight: PropTypes.func, isNavDropdownOpen: PropTypes.bool, isSignedIn: PropTypes.bool, loadCurrentChallenge: PropTypes.func.isRequired, @@ -71,7 +67,6 @@ const propTypes = { toast: PropTypes.object, trackEvent: PropTypes.func.isRequired, updateAppLang: PropTypes.func.isRequired, - updateNavHeight: PropTypes.func, username: PropTypes.string }; @@ -84,7 +79,6 @@ export class FreeCodeCamp extends React.Component { } componentDidMount() { - this.props.initWindowHeight(); if (!this.props.isSignedIn) { this.props.fetchUser(); } @@ -110,7 +104,6 @@ export class FreeCodeCamp extends React.Component { username, points, picture, - updateNavHeight, trackEvent, loadCurrentChallenge, openDropdown, @@ -118,23 +111,22 @@ export class FreeCodeCamp extends React.Component { isNavDropdownOpen } = this.props; const navProps = { - username, - points, - picture, - updateNavHeight, - trackEvent, + closeDropdown, + isNavDropdownOpen, loadCurrentChallenge, openDropdown, - closeDropdown, - isNavDropdownOpen + picture, + points, + trackEvent, + username }; return ( -
+
); diff --git a/common/app/app.less b/common/app/app.less new file mode 100644 index 0000000000..3705a8db5f --- /dev/null +++ b/common/app/app.less @@ -0,0 +1,14 @@ +// should match ./ns.json value and filename +@ns: app; + +.@{ns}-container { + .column(); + width: 100vw; +} + +.@{ns}-content { + .center(@value: @container-xl, @padding: @grid-gutter-width); + // makes the inital content height 0px + // then lets it grow to fit the rest of the space + flex: 1 0 0px; +} diff --git a/common/app/components/Nav/Nav.jsx b/common/app/components/Nav/Nav.jsx index 4c6fa7c9af..033c4af227 100644 --- a/common/app/components/Nav/Nav.jsx +++ b/common/app/components/Nav/Nav.jsx @@ -1,5 +1,4 @@ import React, { PropTypes } from 'react'; -import ReactDOM from 'react-dom'; import { LinkContainer } from 'react-router-bootstrap'; import { Col, @@ -41,7 +40,6 @@ const propTypes = { showLoading: PropTypes.bool, signedIn: PropTypes.bool, trackEvent: PropTypes.func.isRequired, - updateNavHeight: PropTypes.func, username: PropTypes.string }; @@ -55,11 +53,6 @@ export default class FCCNav extends React.Component { }); } - componentDidMount() { - const navBar = ReactDOM.findDOMNode(this); - this.props.updateNavHeight(navBar.clientHeight); - } - handleMapClickOnMap(e) { e.preventDefault(); this.props.trackEvent({ @@ -172,7 +165,7 @@ export default class FCCNav extends React.Component { return ( diff --git a/common/app/index.less b/common/app/index.less new file mode 100644 index 0000000000..69bf5eb725 --- /dev/null +++ b/common/app/index.less @@ -0,0 +1,2 @@ +&{ @import "./app.less"; } +&{ @import "./routes/index.less"; } diff --git a/common/app/ns.json b/common/app/ns.json new file mode 100644 index 0000000000..74999efc4e --- /dev/null +++ b/common/app/ns.json @@ -0,0 +1 @@ +"app" diff --git a/common/app/redux/actions.js b/common/app/redux/actions.js index a4602bc899..6768f58627 100644 --- a/common/app/redux/actions.js +++ b/common/app/redux/actions.js @@ -113,11 +113,6 @@ export const delayedRedirect = createAction(types.delayedRedirect); // hardGoTo(path: String) => Action export const hardGoTo = createAction(types.hardGoTo); -export const initWindowHeight = createAction(types.initWindowHeight); -export const updateWindowHeight = createAction(types.updateWindowHeight); -export const updateNavHeight = createAction(types.updateNavHeight); - - // data export const updateChallengesData = createAction(types.updateChallengesData); export const updateHikesData = createAction(types.updateHikesData); diff --git a/common/app/redux/reducer.js b/common/app/redux/reducer.js index b0e14cb50a..754345ac76 100644 --- a/common/app/redux/reducer.js +++ b/common/app/redux/reducer.js @@ -7,8 +7,6 @@ const initialState = { user: '', lang: '', csrfToken: '', - windowHeight: 0, - navHeight: 0, theme: 'default' }; @@ -41,14 +39,6 @@ export default handleActions( ...state, points }), - [types.updateWindowHeight]: (state, { payload: windowHeight }) => ({ - ...state, - windowHeight - }), - [types.updateNavHeight]: (state, { payload: navHeight }) => ({ - ...state, - navHeight - }), [types.delayedRedirect]: (state, { payload }) => ({ ...state, delayedRedirect: payload diff --git a/common/app/redux/types.js b/common/app/redux/types.js index 1eb0a70635..766b695c85 100644 --- a/common/app/redux/types.js +++ b/common/app/redux/types.js @@ -22,10 +22,6 @@ export default createTypes([ 'hardGoTo', 'delayedRedirect', - 'initWindowHeight', - 'updateWindowHeight', - 'updateNavHeight', - // data handling 'updateChallengesData', 'updateHikesData', diff --git a/common/app/routes/challenges/components/Bug-Modal.jsx b/common/app/routes/challenges/Bug-Modal.jsx similarity index 87% rename from common/app/routes/challenges/components/Bug-Modal.jsx rename to common/app/routes/challenges/Bug-Modal.jsx index f60f2b5019..b22f7d0809 100644 --- a/common/app/routes/challenges/components/Bug-Modal.jsx +++ b/common/app/routes/challenges/Bug-Modal.jsx @@ -2,10 +2,13 @@ import React, { PropTypes } from 'react'; import { connect } from 'react-redux'; import { Button, Modal } from 'react-bootstrap'; import PureComponent from 'react-pure-render/component'; -import { createIssue, openIssueSearch, closeBugModal } from '../redux/actions'; + +import ns from './ns.json'; + +import { createIssue, openIssueSearch, closeBugModal } from './redux/actions'; const mapStateToProps = state => ({ isOpen: state.challengesApp.isBugOpen }); -const actions = { createIssue, openIssueSearch, closeBugModal }; +const mapDispatchToProps = { createIssue, openIssueSearch, closeBugModal }; const bugLink = 'http://forum.freecodecamp.com/t/how-to-report-a-bug/19543'; const propTypes = { @@ -16,7 +19,6 @@ const propTypes = { }; export class BugModal extends PureComponent { - render() { const { isOpen, @@ -28,7 +30,7 @@ export class BugModal extends PureComponent { - + Did you find a bug? + ); + } + return ( +

+ { children || 'Happy Coding!' } + { icon } +
+

+ ); +} + +ChallengeTitle.displayName = 'ChallengeTitle'; +ChallengeTitle.propTypes = propTypes; diff --git a/common/app/routes/challenges/components/CodeMirrorSkeleton.jsx b/common/app/routes/challenges/Code-Mirror-Skeleton.jsx similarity index 99% rename from common/app/routes/challenges/components/CodeMirrorSkeleton.jsx rename to common/app/routes/challenges/Code-Mirror-Skeleton.jsx index a79d220b36..a9fc788d79 100644 --- a/common/app/routes/challenges/components/CodeMirrorSkeleton.jsx +++ b/common/app/routes/challenges/Code-Mirror-Skeleton.jsx @@ -5,6 +5,7 @@ import { Grid, Col, Row } from 'react-bootstrap'; const propTypes = { content: PropTypes.string }; + export default class CodeMirrorSkeleton extends PureComponent { renderLine(line, i) { diff --git a/common/app/routes/challenges/components/Output.jsx b/common/app/routes/challenges/Output.jsx similarity index 81% rename from common/app/routes/challenges/components/Output.jsx rename to common/app/routes/challenges/Output.jsx index 476c4324d4..eb27d3c860 100644 --- a/common/app/routes/challenges/components/Output.jsx +++ b/common/app/routes/challenges/Output.jsx @@ -2,14 +2,15 @@ import React, { PureComponent, PropTypes } from 'react'; import NoSSR from 'react-no-ssr'; import Codemirror from 'react-codemirror'; -import CodeMirrorSkeleton from './CodeMirrorSkeleton.jsx'; +import ns from './ns.json'; +import CodeMirrorSkeleton from './Code-Mirror-Skeleton.jsx'; const defaultOptions = { lineNumbers: false, + lineWrapping: true, mode: 'javascript', - theme: 'monokai', readOnly: 'nocursor', - lineWrapping: true + theme: 'monokai' }; const propTypes = { @@ -21,7 +22,7 @@ export default class Output extends PureComponent { render() { const { output, defaultOutput } = this.props; return ( -
+
}> ; } - - render() { - const { viewType } = this.props; - return ( -
- { this.renderView(viewType) } -
- ); - } } -Challenges.displayName = 'Challenges'; -Challenges.propTypes = propTypes; +Show.displayName = 'Show(ChallengeView)'; +Show.propTypes = propTypes; export default compose( - connect(mapStateToProps, bindableActions), + connect(mapStateToProps, mapDispatchToProps), contain(fetchOptions) -)(Challenges); +)(Show); diff --git a/common/app/routes/challenges/components/Solution-Input.jsx b/common/app/routes/challenges/Solution-Input.jsx similarity index 91% rename from common/app/routes/challenges/components/Solution-Input.jsx rename to common/app/routes/challenges/Solution-Input.jsx index afbf2cdc23..bb242c254f 100644 --- a/common/app/routes/challenges/components/Solution-Input.jsx +++ b/common/app/routes/challenges/Solution-Input.jsx @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { HelpBlock, FormGroup, FormControl } from 'react-bootstrap'; -import { getValidationState, DOMOnlyProps } from '../../../utils/form'; +import { getValidationState, DOMOnlyProps } from '../../utils/form'; const propTypes = { placeholder: PropTypes.string, diff --git a/common/app/routes/challenges/components/Test-Suite.jsx b/common/app/routes/challenges/Test-Suite.jsx similarity index 95% rename from common/app/routes/challenges/components/Test-Suite.jsx rename to common/app/routes/challenges/Test-Suite.jsx index 4d8d7e3269..d11837d911 100644 --- a/common/app/routes/challenges/components/Test-Suite.jsx +++ b/common/app/routes/challenges/Test-Suite.jsx @@ -2,6 +2,8 @@ import React, { PropTypes, PureComponent } from 'react'; import classnames from 'classnames'; import { Col, Row } from 'react-bootstrap'; +import ns from './ns.json'; + const propTypes = { tests: PropTypes.arrayOf(PropTypes.object) }; @@ -41,7 +43,7 @@ export default class TestSuite extends PureComponent { const { tests } = this.props; return (
{ this.renderTests(tests) } diff --git a/common/app/routes/challenges/challenges.less b/common/app/routes/challenges/challenges.less new file mode 100644 index 0000000000..efda7469a8 --- /dev/null +++ b/common/app/routes/challenges/challenges.less @@ -0,0 +1,81 @@ +// should be the same as the filename and ./ns.json +@ns: challenges; + + +.@{ns}-title { + margin-top: 0; + i { + margin-left: 5px; + line-height: 20px; + } +} + +.@{ns}-grayed-out-test-output { + color: @gray-light; +} + +.@{ns}-test-suite { + margin-top: 10px; + & .row { + margin: 0!important; + } + + .big-icon { + font-size: 30px; + } + + .error-icon { + color: @brand-danger; + top: 50%; + } + + .success-icon { + color: @brand-primary; + } + + .refresh-icon { + color: @icon-gray; + } +} + +.night { + .@{ns}-instructions blockquote { + background-color: #242424; + border-color: #515151; + color: #ABABAB + } + .@{ns}-instructions dfn { + background-color: #242424; + color: #02a902; + } + .@{ns}-editor .CodeMirror { + background-color:#242424; + color:#ABABAB; + &-gutters { + background-color:#242424; + color:#ABABAB; + } + .cm-bracket, .cm-tag { + color:#5CAFD6; + } + .cm-property, .cm-string { + color:#B5753A; + } + .cm-keyword, .cm-attribute { + color:#9BBBDC; + } + } + .refresh-icon { + color: @icon-light-gray; + } +} + +.@{ns}-test-output { + font-size: 15px; + font-family: "Ubuntu Mono"; + margin-top: 8px; + line-height:20px; + word-wrap: break-word; +} + +&{ @import "./views/index.less"; } diff --git a/common/app/routes/challenges/components/classic/Preview.jsx b/common/app/routes/challenges/components/classic/Preview.jsx deleted file mode 100644 index ce0fdb1e13..0000000000 --- a/common/app/routes/challenges/components/classic/Preview.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import PureComponent from 'react-pure-render/component'; - -const mainId = 'fcc-main-frame'; - -export default class Preview extends PureComponent { - render() { - return ( -
-
- -
-