Skip to content

Commit

Permalink
Add UI to browse and browse course structure and select the course/ch…
Browse files Browse the repository at this point in the history
…apter/section... to generate a report of
  • Loading branch information
xitij2000 committed Jul 17, 2018
1 parent b7d16b9 commit bf6af13
Show file tree
Hide file tree
Showing 28 changed files with 3,637 additions and 591 deletions.
11 changes: 11 additions & 0 deletions common/static/common/js/components/BlockBrowser/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
extends: 'eslint-config-edx',
root: true,
settings: {
'import/resolver': {
webpack: {
config: 'webpack.dev.config.js',
},
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* global gettext */
import { Button, Icon } from '@edx/paragon';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';

const RightIcon = (
<Icon
className={['fa', 'fa-arrow-right']}
screenReaderText={gettext('View child items')}
/>
);

const UpIcon = (
<Icon
className={['fa', 'fa-arrow-up']}
screenReaderText={gettext('Navigate up')}
/>
);

const BLOCK_TYPE_NAME = {
course: 'Course',
chapter: 'Section',
sequential: 'Sub-section',
vertical: 'Unit',
};

const BlockType = PropTypes.shape({
children: PropTypes.array,
display_name: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
parent: PropTypes.string,
type: PropTypes.string.isRequired,
});

export const BlockList = ({ blocks, selectedBlock, onSelectBlock, onChangeRoot }) => (
<ul className="block-list">
{blocks.map(block => (
<li
key={block.id}
className={classNames(`block-type-${block.type}`, { selected: block.id === selectedBlock })}
>
<Button
className={['block-name']}
onClick={() => onSelectBlock(block.id)}
label={block.display_name}
/>
{block.children &&
<Button
onClick={() => onChangeRoot(block.id)}
label={RightIcon}
/>
}
</li>
))}
</ul>
);

BlockList.propTypes = {
blocks: PropTypes.arrayOf(BlockType),
selectedBlock: PropTypes.string,
onSelectBlock: PropTypes.func.isRequired,
onChangeRoot: PropTypes.func.isRequired,
};

BlockList.defaultProps = {
blocks: null,
selectedBlock: null,
};


export const BlockBrowser = ({ blocks, selectedBlock, onSelectBlock, onChangeRoot, className }) =>
!!blocks && (
<div className={classNames('block-browser', className)}>
<div className="header">
<Button
disabled={!blocks.parent}
onClick={() => blocks.parent && onChangeRoot(blocks.parent)}
label={UpIcon}
/>
<span className="title">
{gettext('Browsing')} {gettext(BLOCK_TYPE_NAME[blocks.type])} &quot;
<a
href="#_"
onClick={(event) => {
event.preventDefault();
onSelectBlock(blocks.id);
}}
title={`${gettext('Select')} ${gettext(BLOCK_TYPE_NAME[blocks.type])}`}
>
{blocks.display_name}
</a>&quot;:
</span>
</div>
<BlockList
blocks={blocks.children}
selectedBlock={selectedBlock}
onSelectBlock={onSelectBlock}
onChangeRoot={onChangeRoot}
/>
</div>
);

BlockBrowser.propTypes = {
blocks: BlockType,
selectedBlock: PropTypes.string,
onSelectBlock: PropTypes.func.isRequired,
onChangeRoot: PropTypes.func.isRequired,
};

BlockBrowser.defaultProps = {
blocks: null,
selectedBlock: null,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* global jest,test,describe,expect */
import React from 'react';
import renderer from 'react-test-renderer';
import { BlockBrowser, BlockList } from './BlockBrowser';
import testBlockTree from './test-block-tree.json';

describe('BlockList component', () => {
test('render with basic parameters', () => {
const component = renderer.create(
<BlockList
blocks={testBlockTree.children}
onSelectBlock={jest.fn()}
selectedBlock={null}
onChangeRoot={jest.fn()}
/>,
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});

describe('BlockBrowser component', () => {
test('render with basic parameters', () => {
const component = renderer.create(
<BlockBrowser
blocks={testBlockTree}
onSelectBlock={jest.fn()}
selectedBlock={null}
onChangeRoot={jest.fn()}
/>,
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

test('render with custom classname', () => {
const component = renderer.create(
<BlockBrowser
blocks={testBlockTree}
className="some-class"
onSelectBlock={jest.fn()}
selectedBlock={null}
onChangeRoot={jest.fn()}
/>,
);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { connect } from 'react-redux';
import { changeRoot } from '../../data/actions/courseBlocks';
import { getActiveBlockTree } from '../../data/selectors/index';
import { BlockBrowser } from './BlockBrowser';

const mapStateToProps = state => ({
blocks: getActiveBlockTree(state),
selectedBlock: state.selectedBlock,
});


const mapDispatchToProps = dispatch => ({
onChangeRoot: blockId => dispatch(changeRoot(blockId)),
});


const BlockBrowserContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(BlockBrowser);

export default BlockBrowserContainer;
Loading

0 comments on commit bf6af13

Please sign in to comment.