Feature(map): Add top level collapse
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { PropTypes } from 'react';
 | 
			
		||||
import PureComponent from 'react-pure-render/component';
 | 
			
		||||
import dedent from 'dedent';
 | 
			
		||||
import SuperBlock from './Super-Block.jsx';
 | 
			
		||||
@@ -44,19 +44,25 @@ const codingPrep = [{
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}];
 | 
			
		||||
 | 
			
		||||
const title = 'Coding Interview Prep';
 | 
			
		||||
const dashedName = 'coding-prep';
 | 
			
		||||
export default class CodingPrep extends PureComponent {
 | 
			
		||||
  static displayName = 'CodingPrep;'
 | 
			
		||||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    mapUi: PropTypes.object,
 | 
			
		||||
    toggleThisPanel: PropTypes.func
 | 
			
		||||
  };
 | 
			
		||||
  render() {
 | 
			
		||||
    const { mapUi, toggleThisPanel } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
      <SuperBlock
 | 
			
		||||
        blocks={ codingPrep }
 | 
			
		||||
        dashedName={ dashedName }
 | 
			
		||||
        mapUi={ mapUi }
 | 
			
		||||
        message={ lockMessage }
 | 
			
		||||
          title='Coding Interview Prep '
 | 
			
		||||
        title={ title }
 | 
			
		||||
        toggleThisPanel={ toggleThisPanel }
 | 
			
		||||
      />
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import React, { PropTypes } from 'react';
 | 
			
		||||
import PureComponent from 'react-pure-render/component';
 | 
			
		||||
import dedent from 'dedent';
 | 
			
		||||
import SuperBlock from './Super-Block.jsx';
 | 
			
		||||
@@ -40,15 +40,24 @@ const nonprofitProjects = {
 | 
			
		||||
  ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const title = 'Full Stack Development Certification';
 | 
			
		||||
const dashedName = 'full-stack';
 | 
			
		||||
export default class FullStack extends PureComponent {
 | 
			
		||||
  static displayName = 'FullStack';
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    mapUi: PropTypes.object,
 | 
			
		||||
    toggleThisPanel: PropTypes.func
 | 
			
		||||
  };
 | 
			
		||||
  render() {
 | 
			
		||||
    const title = 'Full Stack Development Certification';
 | 
			
		||||
    const { mapUi, toggleThisPanel } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <SuperBlock
 | 
			
		||||
        blocks={ [ nonprofitProjects ] }
 | 
			
		||||
        dashedName={ dashedName }
 | 
			
		||||
        mapUi={ mapUi }
 | 
			
		||||
        message={ lockMessage }
 | 
			
		||||
        title={ title }
 | 
			
		||||
        toggleThisPanel={ toggleThisPanel }
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
import React, { PropTypes } from 'react';
 | 
			
		||||
import PureComponent from 'react-pure-render/component';
 | 
			
		||||
import { InputGroup, FormControl, Button, Row } from 'react-bootstrap';
 | 
			
		||||
import classnames from 'classnames';
 | 
			
		||||
 | 
			
		||||
const clearIcon = <i className='fa fa-times' />;
 | 
			
		||||
const searchIcon = <i className='fa fa-search' />;
 | 
			
		||||
const ESC = 27;
 | 
			
		||||
export default class Header extends React.Component {
 | 
			
		||||
export default class Header extends PureComponent {
 | 
			
		||||
  constructor(...props) {
 | 
			
		||||
    super(...props);
 | 
			
		||||
    this.handleKeyDown = this.handleKeyDown.bind(this);
 | 
			
		||||
 
 | 
			
		||||
@@ -11,13 +11,15 @@ import FullStack from './Full-Stack.jsx';
 | 
			
		||||
import CodingPrep from './Coding-Prep.jsx';
 | 
			
		||||
import {
 | 
			
		||||
  clearFilter,
 | 
			
		||||
  updateFilter,
 | 
			
		||||
  fetchChallenges
 | 
			
		||||
  fetchChallenges,
 | 
			
		||||
  toggleThisPanel,
 | 
			
		||||
  updateFilter
 | 
			
		||||
} from '../../redux/actions';
 | 
			
		||||
 | 
			
		||||
const bindableActions = {
 | 
			
		||||
  clearFilter,
 | 
			
		||||
  fetchChallenges,
 | 
			
		||||
  toggleThisPanel,
 | 
			
		||||
  updateFilter
 | 
			
		||||
};
 | 
			
		||||
const superBlocksSelector = createSelector(
 | 
			
		||||
@@ -51,10 +53,12 @@ const superBlocksSelector = createSelector(
 | 
			
		||||
const mapStateToProps = createSelector(
 | 
			
		||||
  superBlocksSelector,
 | 
			
		||||
  state => state.challengesApp.filter,
 | 
			
		||||
  ({ superBlocks }, filter) => {
 | 
			
		||||
  state => state.challengesApp.map,
 | 
			
		||||
  ({ superBlocks }, filter, mapUi) => {
 | 
			
		||||
    return {
 | 
			
		||||
      superBlocks,
 | 
			
		||||
      filter
 | 
			
		||||
      filter,
 | 
			
		||||
      mapUi
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
@@ -72,17 +76,26 @@ export class ShowMap extends PureComponent {
 | 
			
		||||
    clearFilter: PropTypes.func,
 | 
			
		||||
    filter: PropTypes.string,
 | 
			
		||||
    superBlocks: PropTypes.array,
 | 
			
		||||
    updateFilter: PropTypes.func
 | 
			
		||||
    updateFilter: PropTypes.func,
 | 
			
		||||
    mapUi: PropTypes.object
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  renderSuperBlocks(superBlocks, updateCurrentChallenge) {
 | 
			
		||||
  renderSuperBlocks(
 | 
			
		||||
    superBlocks,
 | 
			
		||||
    updateCurrentChallenge,
 | 
			
		||||
    mapUi,
 | 
			
		||||
    toggleThisPanel
 | 
			
		||||
  ) {
 | 
			
		||||
    if (!Array.isArray(superBlocks) || !superBlocks.length) {
 | 
			
		||||
      return <div>No Super Blocks</div>;
 | 
			
		||||
    }
 | 
			
		||||
    return superBlocks.map((superBlock) => {
 | 
			
		||||
    return superBlocks
 | 
			
		||||
      .map((superBlock) => {
 | 
			
		||||
        return (
 | 
			
		||||
          <SuperBlock
 | 
			
		||||
            key={ superBlock.title }
 | 
			
		||||
            mapUi={ mapUi }
 | 
			
		||||
            toggleThisPanel={ toggleThisPanel }
 | 
			
		||||
            updateCurrentChallenge={ updateCurrentChallenge }
 | 
			
		||||
            { ...superBlock }
 | 
			
		||||
          />
 | 
			
		||||
@@ -96,7 +109,9 @@ export class ShowMap extends PureComponent {
 | 
			
		||||
      superBlocks,
 | 
			
		||||
      updateFilter,
 | 
			
		||||
      clearFilter,
 | 
			
		||||
      filter
 | 
			
		||||
      filter,
 | 
			
		||||
      mapUi,
 | 
			
		||||
      toggleThisPanel
 | 
			
		||||
    } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
@@ -105,12 +120,24 @@ export class ShowMap extends PureComponent {
 | 
			
		||||
          filter={ filter }
 | 
			
		||||
          updateFilter={ updateFilter }
 | 
			
		||||
        />
 | 
			
		||||
        <div
 | 
			
		||||
          className='map-accordion'
 | 
			
		||||
          >
 | 
			
		||||
          { this.renderSuperBlocks(superBlocks, updateCurrentChallenge) }
 | 
			
		||||
          <FullStack />
 | 
			
		||||
          <CodingPrep />
 | 
			
		||||
        <div className='map-accordion'>
 | 
			
		||||
          {
 | 
			
		||||
            this.renderSuperBlocks(
 | 
			
		||||
              superBlocks,
 | 
			
		||||
              updateCurrentChallenge,
 | 
			
		||||
              mapUi,
 | 
			
		||||
              toggleThisPanel
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
          <FullStack
 | 
			
		||||
            mapUi={ mapUi }
 | 
			
		||||
            toggleThisPanel={ toggleThisPanel }
 | 
			
		||||
          />
 | 
			
		||||
          <CodingPrep
 | 
			
		||||
            mapUi={ mapUi }
 | 
			
		||||
            toggleThisPanel={ toggleThisPanel }
 | 
			
		||||
          />
 | 
			
		||||
          <div className='spacer' />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
 
 | 
			
		||||
@@ -6,13 +6,25 @@ import { Panel } from 'react-bootstrap';
 | 
			
		||||
import Block from './Block.jsx';
 | 
			
		||||
 | 
			
		||||
export default class SuperBlock extends PureComponent {
 | 
			
		||||
   constructor(...props) {
 | 
			
		||||
     super(...props);
 | 
			
		||||
     this.handleSelect = this.handleSelect.bind(this);
 | 
			
		||||
  }
 | 
			
		||||
  static displayName = 'SuperBlock';
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    title: PropTypes.string,
 | 
			
		||||
    dashedName: PropTypes.string,
 | 
			
		||||
    message: PropTypes.string,
 | 
			
		||||
    blocks: PropTypes.array
 | 
			
		||||
    blocks: PropTypes.array,
 | 
			
		||||
    mapUi: PropTypes.object,
 | 
			
		||||
    toggleThisPanl: PropTypes.func
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  handleSelect(eventKey, e) {
 | 
			
		||||
    e.preventDefault();
 | 
			
		||||
    this.props.toggleThisPanel(eventKey);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderBlocks(blocks) {
 | 
			
		||||
    if (!Array.isArray(blocks) || !blocks.length) {
 | 
			
		||||
      return <div>No Blocks Found</div>;
 | 
			
		||||
@@ -28,15 +40,17 @@ export default class SuperBlock extends PureComponent {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { title, blocks, message } = this.props;
 | 
			
		||||
    const { title, dashedName, blocks, message, mapUi = {} } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <Panel
 | 
			
		||||
        bsClass='map-accordion-panel'
 | 
			
		||||
        collapsible={ true }
 | 
			
		||||
        expanded={ true }
 | 
			
		||||
        eventKey={ dashedName || title }
 | 
			
		||||
        expanded={ dashedName ? mapUi[dashedName] : true }
 | 
			
		||||
        header={ <h2><FA name='caret-right' />{ title }</h2> }
 | 
			
		||||
        id={ title }
 | 
			
		||||
        key={ title }
 | 
			
		||||
        key={ dashedName || title }
 | 
			
		||||
        onSelect={ this.handleSelect }
 | 
			
		||||
        >
 | 
			
		||||
        {
 | 
			
		||||
          message ?
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,29 @@ export const updateFilter = createAction(
 | 
			
		||||
  e => e.target.value
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
function createMapKey(map, key) {
 | 
			
		||||
  map[key] = true;
 | 
			
		||||
  return map;
 | 
			
		||||
}
 | 
			
		||||
export const initMap = createAction(
 | 
			
		||||
  types.initMap,
 | 
			
		||||
  (
 | 
			
		||||
    { superBlock: superBlockMap },
 | 
			
		||||
    superBlocks
 | 
			
		||||
  ) => {
 | 
			
		||||
    if (!superBlocks || !superBlockMap) {
 | 
			
		||||
      return {};
 | 
			
		||||
    }
 | 
			
		||||
    const blocks = superBlocks
 | 
			
		||||
      .map(superBlock => superBlockMap[superBlock].blocks)
 | 
			
		||||
      .reduce((blocks, block) => blocks.concat(block));
 | 
			
		||||
    return superBlocks
 | 
			
		||||
      .concat(blocks)
 | 
			
		||||
      .reduce(createMapKey, {});
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
export const toggleThisPanel = createAction(types.toggleThisPanel);
 | 
			
		||||
 | 
			
		||||
export const clearFilter = createAction(types.clearFilter);
 | 
			
		||||
 | 
			
		||||
// files
 | 
			
		||||
 
 | 
			
		||||
@@ -5,15 +5,16 @@ import {
 | 
			
		||||
  fetchChallenges,
 | 
			
		||||
  replaceChallenge
 | 
			
		||||
} from './types';
 | 
			
		||||
import {
 | 
			
		||||
  fetchChallengeCompleted,
 | 
			
		||||
  fetchChallengesCompleted,
 | 
			
		||||
  updateCurrentChallenge,
 | 
			
		||||
  initMap
 | 
			
		||||
} from './actions';
 | 
			
		||||
import {
 | 
			
		||||
  delayedRedirect,
 | 
			
		||||
  createErrorObserable
 | 
			
		||||
} from '../../../redux/actions';
 | 
			
		||||
import {
 | 
			
		||||
  fetchChallengeCompleted,
 | 
			
		||||
  fetchChallengesCompleted,
 | 
			
		||||
  updateCurrentChallenge
 | 
			
		||||
} from './actions';
 | 
			
		||||
 | 
			
		||||
function createNameIdMap(entities) {
 | 
			
		||||
  const { challenge } = entities;
 | 
			
		||||
@@ -68,11 +69,12 @@ export default function fetchChallengesSaga(action$, getState, { services }) {
 | 
			
		||||
              redirect ? delayedRedirect(redirect) : null
 | 
			
		||||
            );
 | 
			
		||||
          }
 | 
			
		||||
          return Observable.just(
 | 
			
		||||
          return Observable.of(
 | 
			
		||||
            fetchChallengesCompleted(
 | 
			
		||||
              createNameIdMap(entities),
 | 
			
		||||
              result
 | 
			
		||||
            )
 | 
			
		||||
            ),
 | 
			
		||||
            initMap(entities, result),
 | 
			
		||||
          );
 | 
			
		||||
        })
 | 
			
		||||
        .catch(createErrorObserable);
 | 
			
		||||
 
 | 
			
		||||
@@ -46,6 +46,10 @@ const initialState = {
 | 
			
		||||
  legacyKey: '',
 | 
			
		||||
  files: {},
 | 
			
		||||
  // map
 | 
			
		||||
  map: {
 | 
			
		||||
    'full-stack': true,
 | 
			
		||||
    'coding-prep': true
 | 
			
		||||
  },
 | 
			
		||||
  filter: '',
 | 
			
		||||
  superBlocks: [],
 | 
			
		||||
  // misc
 | 
			
		||||
@@ -230,8 +234,36 @@ const filesReducer = handleActions(
 | 
			
		||||
  {}
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// {
 | 
			
		||||
//   // show
 | 
			
		||||
//   [(super)BlockName]: { open: Boolean }
 | 
			
		||||
//   // do not show
 | 
			
		||||
//   [(super)BlockName]: null
 | 
			
		||||
// }
 | 
			
		||||
const mapReducer = handleActions(
 | 
			
		||||
  {
 | 
			
		||||
    [types.initMap]: (state, { payload }) => ({
 | 
			
		||||
      ...state,
 | 
			
		||||
      ...payload
 | 
			
		||||
    }),
 | 
			
		||||
    [types.toggleThisPanel]: (state, { payload }) => ({
 | 
			
		||||
      ...state,
 | 
			
		||||
      [payload]: !state[payload]
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
  initialState.map
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export default function challengeReducers(state, action) {
 | 
			
		||||
  const newState = mainReducer(state, action);
 | 
			
		||||
  const files = filesReducer(state && state.files || {}, action);
 | 
			
		||||
  return newState.files !== files ? { ...newState, files } : newState;
 | 
			
		||||
  if (newState.files !== files) {
 | 
			
		||||
    return { ...newState, files };
 | 
			
		||||
  }
 | 
			
		||||
  // map actions only effect this reducer;
 | 
			
		||||
  const map = mapReducer(state && state.map || {}, action);
 | 
			
		||||
  if (newState.map !== map) {
 | 
			
		||||
    return { ...newState, map };
 | 
			
		||||
  }
 | 
			
		||||
  return newState;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@ export default createTypes([
 | 
			
		||||
  // map
 | 
			
		||||
  'updateFilter',
 | 
			
		||||
  'clearFilter',
 | 
			
		||||
  'initMap',
 | 
			
		||||
  'toggleThisPanel',
 | 
			
		||||
 | 
			
		||||
  // files
 | 
			
		||||
  'updateFile',
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user