Skip to content

Commit fefce09

Browse files
fourcolorsclaydiffrient
authored andcommitted
add columnheader for student list
fixes CNVS-23332 add column headers for student list Change-Id: Ic87a9ae12b45018adbe902f8fab68d00c9ec486f Reviewed-on: https://gerrit.instructure.com/62517 Tested-by: Jenkins Reviewed-by: Clay Diffrient <cdiffrient@instructure.com> QA-Review: Jahnavi Yetukuri <jyetukuri@instructure.com> Product-Review: Clay Diffrient <cdiffrient@instructure.com>
1 parent d30c385 commit fefce09

9 files changed

Lines changed: 383 additions & 7 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/** @jsx React.DOM */
2+
3+
define([
4+
'react',
5+
'./constants'
6+
], function (React, Constants) {
7+
var ModeratedColumnHeader = React.createClass({
8+
displayName: 'ModeratedColumnHeader',
9+
propTypes:{
10+
markColumn: React.PropTypes.number,
11+
currentSortDirection: React.PropTypes.string,
12+
handleSortByThisColumn: React.PropTypes.func.isRequired
13+
},
14+
renderLinkArrow (mark) {
15+
if (mark === this.props.markColumn){
16+
if (this.props.currentSortDirection === Constants.sortDirections.HIGHEST){
17+
return(<i className='icon-mini-arrow-down'></i>);
18+
}else{
19+
return(<i className='icon-mini-arrow-up'></i>);
20+
}
21+
}
22+
},
23+
render () {
24+
return (
25+
<div className='ColumnHeader'>
26+
<div className='ColumnHeader__StudentName'>
27+
<input type='checkbox' />
28+
<strong>Student</strong>
29+
</div>
30+
<div className='ColumnHeader__ColumnItem'>
31+
<a href='#' onClick={this.props.handleSortByThisColumn.bind(null, Constants.markColumn.MARK_ONE, this.props)}>1st Mark {this.renderLinkArrow(Constants.markColumn.MARK_ONE)}</a>
32+
</div>
33+
<div className='ColumnHeader__ColumnItem'>
34+
<a href='#' onClick={this.props.handleSortByThisColumn.bind(null, Constants.markColumn.MARK_TWO, this.props)}>2st Mark {this.renderLinkArrow(Constants.markColumn.MARK_TWO)}</a>
35+
</div>
36+
<div className='ColumnHeader__ColumnItem'>
37+
<a href='#' onClick={this.props.handleSortByThisColumn.bind(null, Constants.markColumn.MARK_THREE, this.props)}>3st Mark {this.renderLinkArrow(Constants.markColumn.MARK_THREE)}</a>
38+
</div>
39+
<div className='ColumnHeader__ColumnItem'>
40+
<span>Grade</span>
41+
</div>
42+
</div>
43+
);
44+
}
45+
});
46+
47+
return ModeratedColumnHeader;
48+
});

app/jsx/assignments/ModerationApp.jsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ define([
66
'./ModeratedStudentList',
77
'./Header',
88
'./FlashMessageHolder',
9-
'./actions/ModerationActions'
10-
], function (React, I18n, ModeratedStudentList, Header, FlashMessageHolder, Actions) {
9+
'./actions/ModerationActions',
10+
'./ModeratedColumnHeader'
11+
], function (React, I18n, ModeratedStudentList, Header, FlashMessageHolder, Actions, ModeratedColumnHeader) {
1112

1213
return React.createClass({
1314
displayName: 'ModerationApp',
@@ -29,15 +30,27 @@ define([
2930
this.setState(this.props.store.getState());
3031
},
3132

33+
handleSortByThisColumn (mark, props) {
34+
this.props.store.dispatch(
35+
Actions.sortMarkColumn(
36+
{
37+
previousMarkColumn: props.markColumn,
38+
markColumn: mark,
39+
currentSortDirection: props.currentSortDirection
40+
}
41+
)
42+
);
43+
},
44+
3245
render () {
3346
return (
3447
<div className='ModerationApp'>
3548
<FlashMessageHolder store={this.props.store} />
3649
<h1 className='screenreader-only'>{I18n.t('Moderate %{assignment_name}', {assignment_name: 'TODO!!!!!!!!'})}</h1>
3750
<Header store={this.props.store} actions={Actions} />
51+
<ModeratedColumnHeader handleSortByThisColumn={this.handleSortByThisColumn} currentSortDirection={this.state.markColumnSort.currentSortDirection} markColumn={this.state.markColumnSort.markColumn} />
3852
<ModeratedStudentList {...this.state} />
3953
</div>
40-
4154
);
4255
}
4356
});

app/jsx/assignments/actions/ModerationActions.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ define([
1717
PUBLISHED_GRADES: 'PUBLISHED_GRADES',
1818
PUBLISHED_GRADES_FAILED: 'PUBLISHED_GRADES_FAILED',
1919
GOT_STUDENTS: 'GOT_STUDENTS',
20+
SORT_MARK_COLUMN: 'SORT_MARK_COLUMN',
21+
22+
sortMarkColumn (markColumnData) {
23+
return {
24+
type: this.SORT_MARK_COLUMN,
25+
payload: markColumnData
26+
}
27+
},
2028

2129
selectStudent (studentId) {
2230
return {

app/jsx/assignments/constants.jsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/** @jsx React.DOM */
2+
3+
define([], function () {
4+
return {
5+
sortDirections: {
6+
HIGHEST: 'HIGHEST',
7+
LOWEST: 'LOWEST'
8+
},
9+
markColumn: {
10+
MARK_ONE: 0,
11+
MARK_TWO: 1,
12+
MARK_THREE: 2
13+
}
14+
}
15+
});
16+

app/jsx/assignments/reducers/rootReducer.jsx

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
define([
44
'underscore',
55
'redux',
6-
'../actions/ModerationActions'
7-
], function (_, Redux, ModerationActions) {
6+
'../actions/ModerationActions',
7+
'./../constants'
8+
], function (_, Redux, ModerationActions, Constants) {
89

910
var { combineReducers } = Redux;
1011

@@ -14,6 +15,35 @@ define([
1415
return state.concat(action.payload.students);
1516
};
1617

18+
studentHandlers[ModerationActions.SORT_MARK_COLUMN] = (state, action) => {
19+
if(action.payload.markColumn == undefined){
20+
return (state || []);
21+
}
22+
23+
// We are just toggling the sort order from what it previously was. If there was no previous
24+
// then we default to sorting by highest/ascending
25+
var studentList = _.sortBy(state, (student) => {
26+
var provisionalGrade = student.provisional_grades[action.payload.markColumn]
27+
if (provisionalGrade) {
28+
return provisionalGrade.score;
29+
}
30+
31+
return 0; // no score is sorted down
32+
})
33+
34+
// if no sort direction has been set, default to descending order
35+
var sortToHighest = (
36+
action.payload.currentSortDirection === Constants.sortDirections.LOWEST) ||
37+
action.payload.currentSortDirection === undefined ||
38+
action.payload.previousMarkColumn != action.payload.markColumn;
39+
40+
if (sortToHighest){
41+
return studentList.reverse();
42+
}
43+
44+
return studentList;
45+
};
46+
1747
var flashHandlers = {};
1848

1949
flashHandlers[ModerationActions.PUBLISHED_GRADES] = (state, action) => {
@@ -74,11 +104,30 @@ define([
74104
return state;
75105
}
76106

107+
function markColumnSort (state, action) {
108+
state = state || {}
109+
if (action.type === ModerationActions.SORT_MARK_COLUMN) {
110+
var newState = _.extend({}, state);
111+
112+
var togglingColumn = action.payload.markColumn === state.markColumn;
113+
var sortDirectionIsHighest = action.payload.currentSortDirection === Constants.sortDirections.HIGHEST;
114+
if(togglingColumn && sortDirectionIsHighest){
115+
newState.currentSortDirection = Constants.sortDirections.LOWEST;
116+
}else{
117+
newState.currentSortDirection = Constants.sortDirections.HIGHEST;
118+
}
119+
newState.markColumn = action.payload.markColumn;
120+
return newState;
121+
}
122+
return state;
123+
}
124+
77125
return combineReducers({
78126
students,
79127
urls,
80128
flashMessage,
81-
assignment
129+
assignment,
130+
markColumnSort
82131
});
83132

84133
});

app/stylesheets/bundles/assignment_moderation.scss

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
@import "base/environment";
22

33
.assignments{
4+
.ColumnHeader{
5+
display: flex;
6+
justify-content: space-between;
7+
align-items: center;
8+
padding-bottom: 5px;
9+
border-bottom: 1px solid #8495A0;
10+
font-size: 11px;
11+
}
12+
13+
.ColumnHeader__StudentName{
14+
input[type='checkbox']{
15+
margin: 0px;
16+
}
17+
strong{
18+
margin-left: 10px;
19+
}
20+
width: 200px;
21+
display: flex;
22+
align-items: center;
23+
}
424

525
.AssignmentList{
626
border-top: 1px solid #F0F0F0;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
define [
2+
'react'
3+
'jsx/assignments/ModeratedColumnHeader'
4+
'jsx/assignments/constants'
5+
], (React, ModeratedColumnHeader, Constants) ->
6+
TestUtils = React.addons.TestUtils
7+
8+
module 'ModeratedColumnHeader',
9+
test 'calls the handleSortByThisColumn fucntion when sort is pressed', ->
10+
callback = sinon.spy()
11+
columnHeader = TestUtils.renderIntoDocument(ModeratedColumnHeader(markColumn: 0, currentSortDirection: Constants.sortDirections.HIGHEST, handleSortByThisColumn: callback ))
12+
headers = TestUtils.scryRenderedDOMComponentsWithClass(columnHeader, 'ColumnHeader__ColumnItem')
13+
link = TestUtils.findRenderedDOMComponentWithTag(headers[0], 'a')
14+
TestUtils.Simulate.click(link.getDOMNode())
15+
ok callback.calledWith(0,{markColumn: 0, currentSortDirection: Constants.sortDirections.HIGHEST, handleSortByThisColumn: callback})
16+
React.unmountComponentAtNode(columnHeader.getDOMNode().parentNode)
17+
18+
test 'displays down arrow when sort direction is highest', ->
19+
columnHeader = TestUtils.renderIntoDocument(ModeratedColumnHeader(markColumn: 0, currentSortDirection: Constants.sortDirections.HIGHEST, handleSortByThisColumn: () => 'nothing here' ))
20+
headers = TestUtils.scryRenderedDOMComponentsWithClass(columnHeader, 'ColumnHeader__ColumnItem')
21+
ok TestUtils. findRenderedDOMComponentWithClass(headers[0], 'icon-mini-arrow-down'), 'finds the up arrow'
22+
React.unmountComponentAtNode(columnHeader.getDOMNode().parentNode)
23+
24+
test 'displays up arrow when sort direction is lowest', ->
25+
columnHeader = TestUtils.renderIntoDocument(ModeratedColumnHeader(markColumn: 0, currentSortDirection: Constants.sortDirections.LOWEST, handleSortByThisColumn: () => 'nothing here' ))
26+
headers = TestUtils.scryRenderedDOMComponentsWithClass(columnHeader, 'ColumnHeader__ColumnItem')
27+
ok TestUtils. findRenderedDOMComponentWithClass(headers[0], 'icon-mini-arrow-up'), 'finds the up arrow'
28+
React.unmountComponentAtNode(columnHeader.getDOMNode().parentNode)

spec/coffeescripts/jsx/assignments/actions/ModerationActionsSpec.coffee

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ define [
55

66
module "ModerationActions - Action Creators",
77

8+
test 'creates the SORT_MARK_COLUMN action', ->
9+
markColumnData =
10+
markColumn: 2,
11+
currentSortDirection: 'highest'
12+
action = ModerationActions.sortMarkColumn(markColumnData)
13+
expected =
14+
type: ModerationActions.SORT_MARK_COLUMN
15+
payload: markColumnData
16+
17+
deepEqual action, expected, "creates the action successfully"
18+
819
test "creates the SELECT_STUDENT action", ->
920
action = ModerationActions.selectStudent(1)
1021
expected =

0 commit comments

Comments
 (0)