diff --git a/common/app/components/Map-Drawer.jsx b/common/app/components/Map-Drawer.jsx index 105586f9eb6..9e287d6c551 100644 --- a/common/app/components/Map-Drawer.jsx +++ b/common/app/components/Map-Drawer.jsx @@ -2,7 +2,7 @@ import React, { PropTypes } from 'react'; import NoSSR from 'react-no-ssr'; import Drawer from './Drawer.jsx'; -import ShowMap from '../routes/challenges/components/map/Show.jsx'; +import ShowMap from '../routes/challenges/components/map/Map.jsx'; export default class MapDrawer extends React.Component { static displayName = 'MapDrawer'; @@ -18,7 +18,7 @@ export default class MapDrawer extends React.Component { + >
{ isAlreadyLoaded || isOpen ? : null } diff --git a/common/app/routes/challenges/components/map/Header.jsx b/common/app/routes/challenges/components/map/Header.jsx new file mode 100644 index 00000000000..8fd2c8e7d44 --- /dev/null +++ b/common/app/routes/challenges/components/map/Header.jsx @@ -0,0 +1,80 @@ +import React, { PropTypes } from 'react'; +import { InputGroup, FormControl, Button, Row } from 'react-bootstrap'; +import classnames from 'classnames'; + +const clearIcon = ; +const searchIcon = ; +const ESC = 27; +export default class Header extends React.Component { + constructor(...props) { + super(...props); + this.handleKeyDown = this.handleKeyDown.bind(this); + } + static displayName = 'MapHeader'; + static propTypes = { + filter: PropTypes.string, + clearFilter: PropTypes.func, + updateFilter: PropTypes.func + }; + + handleKeyDown(e) { + if (e.keyCode === ESC) { + this.props.clearFilter(); + } + } + + renderSearchAddon(filter, clearFilter) { + if (!filter) { + return searchIcon; + } + return { clearIcon }; + } + + render() { + const { + updateFilter, + clearFilter, + filter + } = this.props; + const inputClass = classnames({ + 'map-filter': true, + filled: !!filter + }); + return ( +
+
+

Challenges required for certifications are marked with a *

+ + + + + + + + { this.renderSearchAddon(filter, clearFilter) } + + + +
+
+
+ ); + } +} diff --git a/common/app/routes/challenges/components/map/Map.jsx b/common/app/routes/challenges/components/map/Map.jsx index 8571adfa5dc..e584ebed37b 100644 --- a/common/app/routes/challenges/components/map/Map.jsx +++ b/common/app/routes/challenges/components/map/Map.jsx @@ -1,20 +1,72 @@ import React, { PropTypes } from 'react'; -import classnames from 'classnames'; +import { compose } from 'redux'; +import { contain } from 'redux-epic'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import PureComponent from 'react-pure-render/component'; -import { InputGroup, FormControl, Button, Row } from 'react-bootstrap'; +import MapHeader from './Header.jsx'; import SuperBlock from './Super-Block.jsx'; import FullStack from './Full-Stack.jsx'; import CodingPrep from './Coding-Prep.jsx'; +import { + clearFilter, + updateFilter, + fetchChallenges +} from '../../redux/actions'; -const clearIcon = ; -const searchIcon = ; -const ESC = 27; -export default class ShowMap extends PureComponent { - constructor(...props) { - super(...props); - this.handleKeyDown = this.handleKeyDown.bind(this); +const bindableActions = { + clearFilter, + fetchChallenges, + updateFilter +}; +const superBlocksSelector = createSelector( + state => state.challengesApp.superBlocks, + state => state.entities.superBlock, + state => state.entities.block, + state => state.entities.challenge, + (superBlocks, superBlockMap, blockMap, challengeMap) => { + if (!superBlockMap || !blockMap || !challengeMap) { + return { + superBlocks: [] + }; + } + return { + superBlocks: superBlocks + .map(superBlockName => superBlockMap[superBlockName]) + .map(superBlock => ({ + ...superBlock, + blocks: superBlock.blocks + .map(blockName => blockMap[blockName]) + .map(block => ({ + ...block, + challenges: block.challenges + .map(dashedName => challengeMap[dashedName]) + })) + })) + }; } +); + +const mapStateToProps = createSelector( + superBlocksSelector, + state => state.challengesApp.filter, + ({ superBlocks }, filter) => { + return { + superBlocks, + filter + }; + } +); + +const fetchOptions = { + fetchAction: 'fetchChallenges', + isPrimed({ superBlocks }) { + return Array.isArray(superBlocks) && superBlocks.length > 1; + } +}; + +export class ShowMap extends PureComponent { static displayName = 'Map'; static propTypes = { clearFilter: PropTypes.func, @@ -23,12 +75,6 @@ export default class ShowMap extends PureComponent { updateFilter: PropTypes.func }; - handleKeyDown(e) { - if (e.keyCode === ESC) { - this.props.clearFilter(); - } - } - renderSuperBlocks(superBlocks, updateCurrentChallenge) { if (!Array.isArray(superBlocks) || !superBlocks.length) { return
No Super Blocks
; @@ -44,13 +90,6 @@ export default class ShowMap extends PureComponent { }); } - renderSearchAddon(filter, clearFilter) { - if (!filter) { - return searchIcon; - } - return { clearIcon }; - } - render() { const { updateCurrentChallenge, @@ -59,46 +98,13 @@ export default class ShowMap extends PureComponent { clearFilter, filter } = this.props; - const inputClass = classnames({ - 'map-filter': true, - filled: !!filter - }); return (
-
-
-

Challenges required for certifications are marked with a *

- - - - - - - - { this.renderSearchAddon(filter, clearFilter) } - - - -
-
-
+
@@ -110,3 +116,8 @@ export default class ShowMap extends PureComponent { ); } } + +export default compose( + connect(mapStateToProps, bindableActions), + contain(fetchOptions) +)(ShowMap); diff --git a/common/app/routes/challenges/components/map/Show.jsx b/common/app/routes/challenges/components/map/Show.jsx deleted file mode 100644 index 57a64ec2467..00000000000 --- a/common/app/routes/challenges/components/map/Show.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { PropTypes } from 'react'; -import { compose } from 'redux'; -import { contain } from 'redux-epic'; -import { connect } from 'react-redux'; -import PureComponent from 'react-pure-render/component'; -import { createSelector } from 'reselect'; - -import Map from './Map.jsx'; -import { - clearFilter, - updateFilter, - fetchChallenges -} from '../../redux/actions'; - -const bindableActions = { - clearFilter, - fetchChallenges, - updateFilter -}; - -const superBlocksSelector = createSelector( - state => state.challengesApp.superBlocks, - state => state.entities.superBlock, - state => state.entities.block, - state => state.entities.challenge, - (superBlocks, superBlockMap, blockMap, challengeMap) => { - if (!superBlockMap || !blockMap || !challengeMap) { - return { - superBlocks: [] - }; - } - return { - superBlocks: superBlocks - .map(superBlockName => superBlockMap[superBlockName]) - .map(superBlock => ({ - ...superBlock, - blocks: superBlock.blocks - .map(blockName => blockMap[blockName]) - .map(block => ({ - ...block, - challenges: block.challenges - .map(dashedName => challengeMap[dashedName]) - })) - })) - }; - } -); - -const mapStateToProps = createSelector( - superBlocksSelector, - state => state.challengesApp.filter, - ({ superBlocks }, filter) => { - return { - superBlocks, - filter - }; - } -); - -const fetchOptions = { - fetchAction: 'fetchChallenges', - isPrimed({ superBlocks }) { - return Array.isArray(superBlocks) && superBlocks.length > 1; - } -}; - -export class ShowMap extends PureComponent { - static displayName = 'ShowMap'; - static propTypes = { - clearFilter: PropTypes.func, - filter: PropTypes.string, - superBlocks: PropTypes.array, - updateFilter: PropTypes.func - }; - - render() { - return ( - - ); - } -} - -export default compose( - connect(mapStateToProps, bindableActions), - contain(fetchOptions) -)(ShowMap); diff --git a/common/app/routes/challenges/index.js b/common/app/routes/challenges/index.js index 5dfee030567..dbb97041bbb 100644 --- a/common/app/routes/challenges/index.js +++ b/common/app/routes/challenges/index.js @@ -1,5 +1,5 @@ import Show from './components/Show.jsx'; -import ShowMap from './components/map/Show.jsx'; +import Map from './components/map/Map.jsx'; export const challenges = { path: 'challenges(/:dashedName)', @@ -19,5 +19,5 @@ export const modernChallenges = { export const map = { path: 'map', - component: ShowMap + component: Map };