feature(map): Add collapse to block level
This commit is contained in:
@ -1,97 +1,76 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { Link } from 'react-router';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
import FA from 'react-fontawesome';
|
import FA from 'react-fontawesome';
|
||||||
import PureComponent from 'react-pure-render/component';
|
import PureComponent from 'react-pure-render/component';
|
||||||
import { Panel } from 'react-bootstrap';
|
import { Panel } from 'react-bootstrap';
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
import { updateCurrentChallenge } from '../../redux/actions';
|
import Challenge from './Challenge.jsx';
|
||||||
|
import { toggleThisPanel } from '../../redux/actions';
|
||||||
|
|
||||||
const dispatchActions = { updateCurrentChallenge };
|
const dispatchActions = { toggleThisPanel };
|
||||||
|
const mapStateToProps = createSelector(
|
||||||
|
(_, props) => props.dashedName,
|
||||||
|
state => state.entities.block,
|
||||||
|
state => state.entities.challenge,
|
||||||
|
(state, props) => state.challengesApp.mapUi[props.dashedName],
|
||||||
|
(dashedName, blockMap, challengeMap, isOpen) => {
|
||||||
|
const block = blockMap[dashedName];
|
||||||
|
return {
|
||||||
|
isOpen,
|
||||||
|
dashedName,
|
||||||
|
title: block.title,
|
||||||
|
time: block.time,
|
||||||
|
challenges: block.challenges
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
export class Block extends PureComponent {
|
export class Block extends PureComponent {
|
||||||
|
constructor(...props) {
|
||||||
|
super(...props);
|
||||||
|
this.handleSelect = this.handleSelect.bind(this);
|
||||||
|
}
|
||||||
static displayName = 'Block';
|
static displayName = 'Block';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
dashedName: PropTypes.string,
|
dashedName: PropTypes.string,
|
||||||
time: PropTypes.string,
|
time: PropTypes.string,
|
||||||
|
isOpen: PropTypes.bool,
|
||||||
challenges: PropTypes.array,
|
challenges: PropTypes.array,
|
||||||
updateCurrentChallenge: PropTypes.func
|
toggleThisPanel: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
renderChallenges(blockName, challenges, updateCurrentChallenge) {
|
handleSelect(eventKey, e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.props.toggleThisPanel(eventKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderChallenges(challenges) {
|
||||||
if (!Array.isArray(challenges) || !challenges.length) {
|
if (!Array.isArray(challenges) || !challenges.length) {
|
||||||
return <div>No Challenges Found</div>;
|
return <div>No Challenges Found</div>;
|
||||||
}
|
}
|
||||||
return challenges.map(challenge => {
|
return challenges.map(dashedName => (
|
||||||
const {
|
<Challenge
|
||||||
title,
|
dashedName={ dashedName }
|
||||||
dashedName,
|
key={ dashedName }
|
||||||
isLocked,
|
/>
|
||||||
isRequired,
|
));
|
||||||
isCompleted
|
|
||||||
} = challenge;
|
|
||||||
const challengeClassName = classnames({
|
|
||||||
'text-primary': true,
|
|
||||||
'padded-ionic-icon': true,
|
|
||||||
'negative-15': true,
|
|
||||||
'challenge-title': true,
|
|
||||||
'ion-checkmark-circled faded': !isLocked && isCompleted,
|
|
||||||
'ion-ios-circle-outline': !isLocked && !isCompleted,
|
|
||||||
'ion-locked': isLocked,
|
|
||||||
disabled: isLocked
|
|
||||||
});
|
|
||||||
if (isLocked) {
|
|
||||||
return (
|
|
||||||
<p
|
|
||||||
className={ challengeClassName }
|
|
||||||
key={ title }
|
|
||||||
>
|
|
||||||
{ title }
|
|
||||||
{
|
|
||||||
isRequired ?
|
|
||||||
<span className='text-primary'><strong>*</strong></span> :
|
|
||||||
''
|
|
||||||
}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<p
|
|
||||||
className={ challengeClassName }
|
|
||||||
key={ title }
|
|
||||||
>
|
|
||||||
<Link to={ `/challenges/${blockName}/${dashedName}` }>
|
|
||||||
<span
|
|
||||||
onClick={ () => updateCurrentChallenge(challenge) }
|
|
||||||
>
|
|
||||||
{ title }
|
|
||||||
<span className='sr-only'>complete</span>
|
|
||||||
{
|
|
||||||
isRequired ?
|
|
||||||
<span className='text-primary'><strong>*</strong></span> :
|
|
||||||
''
|
|
||||||
}
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
title,
|
title,
|
||||||
time,
|
time,
|
||||||
challenges,
|
dashedName,
|
||||||
updateCurrentChallenge,
|
isOpen,
|
||||||
dashedName
|
challenges
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<Panel
|
<Panel
|
||||||
bsClass='map-accordion-panel-nested'
|
bsClass='map-accordion-panel-nested'
|
||||||
collapsible={ true }
|
collapsible={ true }
|
||||||
expanded={ false }
|
eventKey={ dashedName || title }
|
||||||
|
expanded={ isOpen }
|
||||||
header={
|
header={
|
||||||
<div>
|
<div>
|
||||||
<h3><FA name='caret-right' />{ title }</h3>
|
<h3><FA name='caret-right' />{ title }</h3>
|
||||||
@ -100,13 +79,12 @@ export class Block extends PureComponent {
|
|||||||
}
|
}
|
||||||
id={ title }
|
id={ title }
|
||||||
key={ title }
|
key={ title }
|
||||||
|
onSelect={ this.handleSelect }
|
||||||
>
|
>
|
||||||
{
|
{ this.renderChallenges(challenges) }
|
||||||
this.renderChallenges(dashedName, challenges, updateCurrentChallenge)
|
|
||||||
}
|
|
||||||
</Panel>
|
</Panel>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(null, dispatchActions)(Block);
|
export default connect(mapStateToProps, dispatchActions)(Block);
|
||||||
|
110
common/app/routes/challenges/components/map/Challenge.jsx
Normal file
110
common/app/routes/challenges/components/map/Challenge.jsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import React, { PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
|
import { Link } from 'react-router';
|
||||||
|
import PureComponent from 'react-pure-render/component';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import { updateCurrentChallenge } from '../../redux/actions';
|
||||||
|
|
||||||
|
const bindableActions = { updateCurrentChallenge };
|
||||||
|
const mapStateToProps = createSelector(
|
||||||
|
(_, props) => props.dashedName,
|
||||||
|
state => state.entities.challenge,
|
||||||
|
(dashedName, challengeMap) => {
|
||||||
|
const challenge = challengeMap[dashedName] || {};
|
||||||
|
return {
|
||||||
|
dashedName,
|
||||||
|
challenge,
|
||||||
|
title: challenge.title,
|
||||||
|
block: challenge.block,
|
||||||
|
isLocked: challenge.isLocked,
|
||||||
|
isRequired: challenge.isRequired,
|
||||||
|
isCompleted: challenge.isCompleted
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
export class Challenge extends PureComponent {
|
||||||
|
constructor(...args) {
|
||||||
|
super(...args);
|
||||||
|
}
|
||||||
|
static displayName = 'Challenge';
|
||||||
|
static propTypes = {
|
||||||
|
title: PropTypes.string,
|
||||||
|
dashedName: PropTypes.string,
|
||||||
|
block: PropTypes.string,
|
||||||
|
isLocked: PropTypes.bool,
|
||||||
|
isRequired: PropTypes.bool,
|
||||||
|
isCompleted: PropTypes.bool,
|
||||||
|
challenge: PropTypes.object,
|
||||||
|
updateCurrentChallenge: PropTypes.func
|
||||||
|
};
|
||||||
|
|
||||||
|
renderCompleted(isCompleted, isLocked) {
|
||||||
|
if (isLocked || !isCompleted) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return <span className='sr-only'>completed</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRequired(isRequired) {
|
||||||
|
if (!isRequired) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return <span className='text-primary'><strong>*</strong></span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLocked(title, isRequired, className) {
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
className={ className }
|
||||||
|
key={ title }
|
||||||
|
>
|
||||||
|
{ title }
|
||||||
|
{ this.renderRequired(isRequired) }
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
dashedName,
|
||||||
|
block,
|
||||||
|
isLocked,
|
||||||
|
isRequired,
|
||||||
|
isCompleted,
|
||||||
|
challenge,
|
||||||
|
updateCurrentChallenge
|
||||||
|
} = this.props;
|
||||||
|
const challengeClassName = classnames({
|
||||||
|
'text-primary': true,
|
||||||
|
'padded-ionic-icon': true,
|
||||||
|
'negative-15': true,
|
||||||
|
'challenge-title': true,
|
||||||
|
'ion-checkmark-circled faded': !isLocked && isCompleted,
|
||||||
|
'ion-ios-circle-outline': !isLocked && !isCompleted,
|
||||||
|
'ion-locked': isLocked,
|
||||||
|
disabled: isLocked
|
||||||
|
});
|
||||||
|
if (isLocked) {
|
||||||
|
return this.renderLocked(title, isRequired, challengeClassName);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<p
|
||||||
|
className={ challengeClassName }
|
||||||
|
key={ title }
|
||||||
|
>
|
||||||
|
<Link to={ `/challenges/${block}/${dashedName}` }>
|
||||||
|
<span onClick={ () => updateCurrentChallenge(challenge) }>
|
||||||
|
{ title }
|
||||||
|
{ this.renderCompleted(isCompleted, isLocked) }
|
||||||
|
{ this.renderRequired(isRequired) }
|
||||||
|
</span>
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, bindableActions)(Challenge);
|
@ -15,26 +15,31 @@ const nonprofitProjects = {
|
|||||||
challenges: [
|
challenges: [
|
||||||
{
|
{
|
||||||
title: 'Greenfield Nonprofit Project #1',
|
title: 'Greenfield Nonprofit Project #1',
|
||||||
|
dashedName: 'greenfield-1',
|
||||||
isLocked: true,
|
isLocked: true,
|
||||||
isRequired: true
|
isRequired: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Greenfield Nonprofit Project #2',
|
title: 'Greenfield Nonprofit Project #2',
|
||||||
|
dashedName: 'greenfield-2',
|
||||||
isLocked: true,
|
isLocked: true,
|
||||||
isRequired: true
|
isRequired: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Legacy Code Nonprofit Project #1',
|
title: 'Legacy Code Nonprofit Project #1',
|
||||||
|
dashedName: 'legacy-1',
|
||||||
isLocked: true,
|
isLocked: true,
|
||||||
isRequired: true
|
isRequired: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Legacy Code Nonprofit Project #2',
|
title: 'Legacy Code Nonprofit Project #2',
|
||||||
|
dashedName: 'legacy-2',
|
||||||
isLocked: true,
|
isLocked: true,
|
||||||
isRequired: true
|
isRequired: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Claim your Full Stack Development Certification',
|
title: 'Claim your Full Stack Development Certification',
|
||||||
|
dashedName: 'claim-full-stack',
|
||||||
isLocked: true
|
isLocked: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,20 +1,24 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import PureComponent from 'react-pure-render/component';
|
import PureComponent from 'react-pure-render/component';
|
||||||
import { InputGroup, FormControl, Button, Row } from 'react-bootstrap';
|
import { InputGroup, FormControl, Button, Row } from 'react-bootstrap';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
import { clearFilter, updateFilter } from '../../redux/actions';
|
||||||
|
|
||||||
|
const ESC = 27;
|
||||||
const clearIcon = <i className='fa fa-times' />;
|
const clearIcon = <i className='fa fa-times' />;
|
||||||
const searchIcon = <i className='fa fa-search' />;
|
const searchIcon = <i className='fa fa-search' />;
|
||||||
const ESC = 27;
|
const bindableActions = { clearFilter, updateFilter };
|
||||||
export default class Header extends PureComponent {
|
const mapStateToProps = state => ({ filter: state.challengesApp.filter });
|
||||||
|
export class Header extends PureComponent {
|
||||||
constructor(...props) {
|
constructor(...props) {
|
||||||
super(...props);
|
super(...props);
|
||||||
this.handleKeyDown = this.handleKeyDown.bind(this);
|
this.handleKeyDown = this.handleKeyDown.bind(this);
|
||||||
}
|
}
|
||||||
static displayName = 'MapHeader';
|
static displayName = 'MapHeader';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
filter: PropTypes.string,
|
|
||||||
clearFilter: PropTypes.func,
|
clearFilter: PropTypes.func,
|
||||||
|
filter: PropTypes.string,
|
||||||
updateFilter: PropTypes.func
|
updateFilter: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -79,3 +83,4 @@ export default class Header extends PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export default connect(mapStateToProps, bindableActions)(Header);
|
||||||
|
@ -2,141 +2,45 @@ import React, { PropTypes } from 'react';
|
|||||||
import { compose } from 'redux';
|
import { compose } from 'redux';
|
||||||
import { contain } from 'redux-epic';
|
import { contain } from 'redux-epic';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
|
||||||
import PureComponent from 'react-pure-render/component';
|
import PureComponent from 'react-pure-render/component';
|
||||||
|
|
||||||
import MapHeader from './Header.jsx';
|
import MapHeader from './Header.jsx';
|
||||||
import SuperBlock from './Super-Block.jsx';
|
import SuperBlock from './Super-Block.jsx';
|
||||||
import FullStack from './Full-Stack.jsx';
|
import { fetchChallenges } from '../../redux/actions';
|
||||||
import CodingPrep from './Coding-Prep.jsx';
|
|
||||||
import {
|
|
||||||
clearFilter,
|
|
||||||
fetchChallenges,
|
|
||||||
toggleThisPanel,
|
|
||||||
updateFilter
|
|
||||||
} from '../../redux/actions';
|
|
||||||
|
|
||||||
const bindableActions = {
|
|
||||||
clearFilter,
|
|
||||||
fetchChallenges,
|
|
||||||
toggleThisPanel,
|
|
||||||
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,
|
|
||||||
state => state.challengesApp.map,
|
|
||||||
({ superBlocks }, filter, mapUi) => {
|
|
||||||
return {
|
|
||||||
superBlocks,
|
|
||||||
filter,
|
|
||||||
mapUi
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
const bindableActions = { fetchChallenges };
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
superBlocks: state.challengesApp.superBlocks
|
||||||
|
});
|
||||||
const fetchOptions = {
|
const fetchOptions = {
|
||||||
fetchAction: 'fetchChallenges',
|
fetchAction: 'fetchChallenges',
|
||||||
isPrimed({ superBlocks }) {
|
isPrimed({ superBlocks }) {
|
||||||
return Array.isArray(superBlocks) && superBlocks.length > 1;
|
return Array.isArray(superBlocks) && superBlocks.length > 1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ShowMap extends PureComponent {
|
export class ShowMap extends PureComponent {
|
||||||
static displayName = 'Map';
|
static displayName = 'Map';
|
||||||
static propTypes = {
|
static propTypes = { superBlocks: PropTypes.array };
|
||||||
clearFilter: PropTypes.func,
|
|
||||||
filter: PropTypes.string,
|
|
||||||
superBlocks: PropTypes.array,
|
|
||||||
updateFilter: PropTypes.func,
|
|
||||||
mapUi: PropTypes.object
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSuperBlocks(
|
renderSuperBlocks(superBlocks) {
|
||||||
superBlocks,
|
|
||||||
updateCurrentChallenge,
|
|
||||||
mapUi,
|
|
||||||
toggleThisPanel
|
|
||||||
) {
|
|
||||||
if (!Array.isArray(superBlocks) || !superBlocks.length) {
|
if (!Array.isArray(superBlocks) || !superBlocks.length) {
|
||||||
return <div>No Super Blocks</div>;
|
return <div>No Super Blocks</div>;
|
||||||
}
|
}
|
||||||
return superBlocks
|
return superBlocks.map(dashedName => (
|
||||||
.map((superBlock) => {
|
<SuperBlock
|
||||||
return (
|
dashedName={ dashedName }
|
||||||
<SuperBlock
|
key={ dashedName }
|
||||||
key={ superBlock.title }
|
/>
|
||||||
mapUi={ mapUi }
|
));
|
||||||
toggleThisPanel={ toggleThisPanel }
|
|
||||||
updateCurrentChallenge={ updateCurrentChallenge }
|
|
||||||
{ ...superBlock }
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const { superBlocks } = this.props;
|
||||||
updateCurrentChallenge,
|
|
||||||
superBlocks,
|
|
||||||
updateFilter,
|
|
||||||
clearFilter,
|
|
||||||
filter,
|
|
||||||
mapUi,
|
|
||||||
toggleThisPanel
|
|
||||||
} = this.props;
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<MapHeader
|
<MapHeader />
|
||||||
clearFilter={ clearFilter }
|
|
||||||
filter={ filter }
|
|
||||||
updateFilter={ updateFilter }
|
|
||||||
/>
|
|
||||||
<div className='map-accordion'>
|
<div className='map-accordion'>
|
||||||
{
|
{ this.renderSuperBlocks(superBlocks) }
|
||||||
this.renderSuperBlocks(
|
|
||||||
superBlocks,
|
|
||||||
updateCurrentChallenge,
|
|
||||||
mapUi,
|
|
||||||
toggleThisPanel
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<FullStack
|
|
||||||
mapUi={ mapUi }
|
|
||||||
toggleThisPanel={ toggleThisPanel }
|
|
||||||
/>
|
|
||||||
<CodingPrep
|
|
||||||
mapUi={ mapUi }
|
|
||||||
toggleThisPanel={ toggleThisPanel }
|
|
||||||
/>
|
|
||||||
<div className='spacer' />
|
<div className='spacer' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,36 @@
|
|||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { createSelector } from 'reselect';
|
||||||
import PureComponent from 'react-pure-render/component';
|
import PureComponent from 'react-pure-render/component';
|
||||||
import FA from 'react-fontawesome';
|
import FA from 'react-fontawesome';
|
||||||
import { Panel } from 'react-bootstrap';
|
import { Panel } from 'react-bootstrap';
|
||||||
|
|
||||||
import Block from './Block.jsx';
|
import Block from './Block.jsx';
|
||||||
|
import { toggleThisPanel } from '../../redux/actions';
|
||||||
|
|
||||||
export default class SuperBlock extends PureComponent {
|
const dispatchActions = { toggleThisPanel };
|
||||||
constructor(...props) {
|
const mapStateToProps = createSelector(
|
||||||
super(...props);
|
(_, props) => props.dashedName,
|
||||||
this.handleSelect = this.handleSelect.bind(this);
|
state => state.entities.superBlock,
|
||||||
|
(state, props) => state.challengesApp.mapUi[props.dashedName],
|
||||||
|
(dashedName, superBlockMap, isOpen) => ({
|
||||||
|
isOpen,
|
||||||
|
title: superBlockMap[dashedName].title,
|
||||||
|
blocks: superBlockMap[dashedName].blocks
|
||||||
|
})
|
||||||
|
);
|
||||||
|
export class SuperBlock extends PureComponent {
|
||||||
|
constructor(...props) {
|
||||||
|
super(...props);
|
||||||
|
this.handleSelect = this.handleSelect.bind(this);
|
||||||
}
|
}
|
||||||
static displayName = 'SuperBlock';
|
static displayName = 'SuperBlock';
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
dashedName: PropTypes.string,
|
dashedName: PropTypes.string,
|
||||||
message: PropTypes.string,
|
|
||||||
blocks: PropTypes.array,
|
blocks: PropTypes.array,
|
||||||
mapUi: PropTypes.object,
|
isOpen: PropTypes.bool,
|
||||||
|
message: PropTypes.string,
|
||||||
toggleThisPanl: PropTypes.func
|
toggleThisPanl: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,36 +43,45 @@ export default class SuperBlock extends PureComponent {
|
|||||||
if (!Array.isArray(blocks) || !blocks.length) {
|
if (!Array.isArray(blocks) || !blocks.length) {
|
||||||
return <div>No Blocks Found</div>;
|
return <div>No Blocks Found</div>;
|
||||||
}
|
}
|
||||||
return blocks.map(block => {
|
return blocks.map(dashedName => (
|
||||||
return (
|
<Block
|
||||||
<Block
|
dashedName={ dashedName }
|
||||||
key={ block.title }
|
key={ dashedName }
|
||||||
{ ...block }
|
/>
|
||||||
/>
|
));
|
||||||
);
|
}
|
||||||
});
|
|
||||||
|
renderMessage(message) {
|
||||||
|
if (!message) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className='challenge-block-description'>
|
||||||
|
{ message }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { title, dashedName, blocks, message, mapUi = {} } = this.props;
|
const {
|
||||||
|
title,
|
||||||
|
dashedName,
|
||||||
|
blocks,
|
||||||
|
message,
|
||||||
|
isOpen
|
||||||
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<Panel
|
<Panel
|
||||||
bsClass='map-accordion-panel'
|
bsClass='map-accordion-panel'
|
||||||
collapsible={ true }
|
collapsible={ true }
|
||||||
eventKey={ dashedName || title }
|
eventKey={ dashedName || title }
|
||||||
expanded={ dashedName ? mapUi[dashedName] : true }
|
expanded={ isOpen }
|
||||||
header={ <h2><FA name='caret-right' />{ title }</h2> }
|
header={ <h2><FA name='caret-right' />{ title }</h2> }
|
||||||
id={ title }
|
id={ title }
|
||||||
key={ dashedName || title }
|
key={ dashedName || title }
|
||||||
onSelect={ this.handleSelect }
|
onSelect={ this.handleSelect }
|
||||||
>
|
>
|
||||||
{
|
{ this.renderMessage(message) }
|
||||||
message ?
|
|
||||||
<div className='challenge-block-description'>
|
|
||||||
{ message }
|
|
||||||
</div> :
|
|
||||||
''
|
|
||||||
}
|
|
||||||
<div
|
<div
|
||||||
className='map-accordion-block'
|
className='map-accordion-block'
|
||||||
>
|
>
|
||||||
@ -68,3 +91,8 @@ export default class SuperBlock extends PureComponent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
dispatchActions
|
||||||
|
)(SuperBlock);
|
||||||
|
@ -46,10 +46,7 @@ const initialState = {
|
|||||||
legacyKey: '',
|
legacyKey: '',
|
||||||
files: {},
|
files: {},
|
||||||
// map
|
// map
|
||||||
map: {
|
mapUi: {},
|
||||||
'full-stack': true,
|
|
||||||
'coding-prep': true
|
|
||||||
},
|
|
||||||
filter: '',
|
filter: '',
|
||||||
superBlocks: [],
|
superBlocks: [],
|
||||||
// misc
|
// misc
|
||||||
@ -251,7 +248,7 @@ const mapReducer = handleActions(
|
|||||||
[payload]: !state[payload]
|
[payload]: !state[payload]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
initialState.map
|
initialState.mapUi
|
||||||
);
|
);
|
||||||
|
|
||||||
export default function challengeReducers(state, action) {
|
export default function challengeReducers(state, action) {
|
||||||
@ -261,9 +258,9 @@ export default function challengeReducers(state, action) {
|
|||||||
return { ...newState, files };
|
return { ...newState, files };
|
||||||
}
|
}
|
||||||
// map actions only effect this reducer;
|
// map actions only effect this reducer;
|
||||||
const map = mapReducer(state && state.map || {}, action);
|
const mapUi = mapReducer(state && state.mapUi || {}, action);
|
||||||
if (newState.map !== map) {
|
if (newState.mapUi !== mapUi) {
|
||||||
return { ...newState, map };
|
return { ...newState, mapUi };
|
||||||
}
|
}
|
||||||
return newState;
|
return newState;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user