Skip to content
This repository has been archived by the owner on Jan 28, 2020. It is now read-only.

Commit

Permalink
Modified learningresource panel to include multi select
Browse files Browse the repository at this point in the history
  • Loading branch information
Giovanni Di Milia committed Jul 30, 2015
1 parent 905cea0 commit b4e3321
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 52 deletions.
72 changes: 49 additions & 23 deletions ui/jstests/test-learning-resource.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
define(['QUnit', 'jquery', 'learning_resources', 'reactaddons',
define(['QUnit', 'jquery', 'reactaddons', 'lodash', 'learning_resources',
'test_utils', 'jquery_mockjax'], function(
QUnit, $, LearningResources, React, TestUtils) {
QUnit, $, React, _, LearningResources, TestUtils) {
'use strict';

var VocabularyOption = LearningResources.VocabularyOption;
Expand Down Expand Up @@ -41,6 +41,12 @@ define(['QUnit', 'jquery', 'learning_resources', 'reactaddons',
"label": "required",
"weight": 1
};
var prereqTermsResponseNotRequired = {
"id": 4,
"slug": "notrequired",
"label": "notrequired",
"weight": 1
};
var vocabularyResponsePrereq = {
"id": 1,
"slug": "prerequisite",
Expand All @@ -49,7 +55,8 @@ define(['QUnit', 'jquery', 'learning_resources', 'reactaddons',
"vocabulary_type": "m",
"required": false,
"weight": 2147483647,
"terms": [prereqTermsResponseRequired]
"terms": [prereqTermsResponseRequired, prereqTermsResponseNotRequired],
"multi_terms": true
};
var vocabularyResponseDifficulty = {
"id": 1,
Expand All @@ -59,7 +66,8 @@ define(['QUnit', 'jquery', 'learning_resources', 'reactaddons',
"vocabulary_type": "f",
"required": false,
"weight": 2147483647,
"terms": [termResponseEasy, termResponseHard]
"terms": [termResponseEasy, termResponseHard],
"multi_terms": false
};
var vocabulariesResponseFirst = {
"count": 1,
Expand Down Expand Up @@ -123,13 +131,11 @@ define(['QUnit', 'jquery', 'learning_resources', 'reactaddons',

// two terms
var $termsSelect = $vocabSelect.find("option");
assert.equal($termsSelect.size(), 3);
assert.equal($termsSelect[0].text, "");
assert.equal($termsSelect[0].value, "");
assert.equal($termsSelect[1].text, "easy");
assert.equal($termsSelect[1].value, "easy");
assert.equal($termsSelect[2].text, "hard");
assert.equal($termsSelect[2].value, "hard");
assert.equal($termsSelect.size(), 2);
assert.equal($termsSelect[0].text, "easy");
assert.equal($termsSelect[0].value, "easy");
assert.equal($termsSelect[1].text, "hard");
assert.equal($termsSelect[1].value, "hard");

done();
};
Expand Down Expand Up @@ -158,27 +164,47 @@ define(['QUnit', 'jquery', 'learning_resources', 'reactaddons',

// two terms, first vocab
var $terms1Select = $($vocabSelect[0]).find("option");
assert.equal($terms1Select.size(), 3);
assert.equal($terms1Select[0].selected, true);
assert.equal($terms1Select[1].selected, false);
assert.equal($terms1Select[2].selected, false);
assert.equal($terms1Select.size(), 2);
assert.equal($terms1Select[0].selected, false);
assert.equal($terms1Select[0].selected, false);

// TestUtils.Simulate.change only simulates a change event,
// we need to update the value first ourselves
$vocabSelect[0].value = "hard";
$($vocabSelect[0]).val("hard").trigger('change');
React.addons.TestUtils.Simulate.change($vocabSelect[0]);
component.forceUpdate(function() {
assert.equal($terms1Select[0].selected, false);
assert.equal($terms1Select[1].selected, false);
assert.equal($terms1Select[2].selected, true);

assert.equal($terms1Select[1].selected, true);
// make sure second vocab (which has a default value) is set properly
var $terms2Select = $($vocabSelect[1]).find("option");
assert.equal($terms2Select.size(), 2);
assert.equal($terms2Select[0].selected, false);
assert.equal($terms2Select[1].selected, true);

done();
assert.equal($terms2Select[0].selected, true);
assert.equal($terms2Select[1].selected, false);
//the second vocabulary can be a multi select
$($vocabSelect[1])
.val(["notrequired", "required"])
.trigger('change');
React.addons.TestUtils.Simulate.change($vocabSelect[1]);
component.forceUpdate(function() {
assert.equal($terms2Select[0].selected, true);
assert.equal($terms2Select[1].selected, true);
//be sure that the state reflects the selection
var terms = _.map(component.state.vocabulariesAndTerms,
function (tuple) {
return tuple.selectedTerms;
});
terms = _.flatten(terms);
terms.sort();
var expectedTerms = [
$terms1Select[1].value,
$terms2Select[0].value,
$terms2Select[1].value
];
expectedTerms.sort();
assert.deepEqual(terms, expectedTerms);

done();
});
});
});
};
Expand Down
82 changes: 53 additions & 29 deletions ui/static/ui/js/learning_resources.jsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,63 @@
define('learning_resources', [
'reactaddons', 'jquery', 'lodash', 'utils'], function (
'reactaddons', 'jquery', 'lodash', 'utils', 'select2'], function (
React, $, _, Utils) {
'use strict';

var StatusBox = Utils.StatusBox;

var VocabularyOption = React.createClass({
render: function () {
applySelect2: function () {
//this function can be used only if the component has been mounted
var options = _.map(this.props.terms, function (term) {
return <option key={term.slug}
value={term.slug}>
{term.label}
</option>;
return {
id: term.slug,
text: term.label
};
});

var thiz = this;
var isMultiTerms = this.props.vocabulary.multi_terms;
//not allowing the clear for the multi terms dropdown
var allowClear = !isMultiTerms;
//apply select2 to the right elements
$(React.findDOMNode(this)).find('select').select2({
multiple: isMultiTerms,
data: options,
placeholder: "Select a value for " + this.props.vocabulary.name,
allowClear: allowClear,
theme: "bootstrap",
})
.val(this.props.selectedTerms)
.trigger('change')
.on('change', function () {
var selectValues = [].concat($(this).val());
//if there are single select dropdowns, .val() can return null
selectValues = _.filter(selectValues, function(value) {
return value !== null;
});
thiz.props.updateTerms(thiz.props.vocabulary.slug, selectValues);
});
},
render: function () {
return <div className="form-group">
<label className="col-sm-6 control-label">
{this.props.vocabulary.name}
</label>
<div className="col-sm-6">
<select className="form-control" value={this.props.selectedTerm}
onChange={this.handleChange}>
<option key="" value=""></option>
{options}
<select className="form-control">
</select>
</div>
</div>;
},
handleChange: function(e) {
this.props.updateTerm(this.props.vocabulary.slug, e.target.value);
componentDidMount: function() {
this.applySelect2();
},
componentDidChange: function() {
this.applySelect2();
},
componentWillChange: function() {
//before react applies the changes need to detach the event handler
//from the select node
$(React.findDOMNode(this)).find('select').off('change');
}
});

Expand All @@ -38,14 +67,14 @@ define('learning_resources', [
var thiz = this;
var vocabulariesAndTerms = this.state.vocabulariesAndTerms;
var options = _.map(vocabulariesAndTerms, function (pair) {
var updateTerm = function(vocabSlug, newTermSlug) {
var updateTerms = function(vocabSlug, newTermsSlug) {
var newVocabulariesAndTerms = _.map(vocabulariesAndTerms,
function(tuple) {
if (vocabSlug === tuple.vocabulary.slug) {
return {
vocabulary: tuple.vocabulary,
terms: tuple.terms,
selectedTerm: newTermSlug
selectedTerms: newTermsSlug
};
} else {
return tuple;
Expand All @@ -60,9 +89,9 @@ define('learning_resources', [
return <VocabularyOption
vocabulary={pair.vocabulary}
terms={pair.terms}
selectedTerm={pair.selectedTerm}
selectedTerms={pair.selectedTerms}
key={pair.vocabulary.slug}
updateTerm={updateTerm}
updateTerms={updateTerms}
/>;
});

Expand Down Expand Up @@ -108,12 +137,9 @@ define('learning_resources', [
var thiz = this;

var terms = _.map(this.state.vocabulariesAndTerms, function (tuple) {
return tuple.selectedTerm;
return tuple.selectedTerms;
});
terms = _.filter(terms, function(selectedTerm) {
return selectedTerm;
});

terms = _.flatten(terms);
var data = {
terms: terms,
description: this.state.description
Expand Down Expand Up @@ -159,7 +185,6 @@ define('learning_resources', [
description: description,
previewUrl: previewUrl,
});

Utils.getVocabulariesAndTerms(
thiz.props.repoSlug, learningResourceType)
.then(function (results) {
Expand All @@ -172,15 +197,14 @@ define('learning_resources', [
function(tuple) {
var vocabulary = tuple.vocabulary;
var terms = tuple.terms;

var selectedTerm = _.result(_.find(terms, function(term) {
return _.includes(selectedTerms, term.slug);
}), 'slug');

var selectedTermsInVocab = _.pluck(
_.filter(terms, function(term) {
return _.includes(selectedTerms, term.slug);
}), 'slug');
return {
vocabulary: vocabulary,
terms: terms,
selectedTerm: selectedTerm
selectedTerms: selectedTermsInVocab
};
});

Expand Down
1 change: 1 addition & 0 deletions ui/static/ui/js/require_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var REQUIRE_PATHS = {
react: "react/react",
reactaddons: "react/react-with-addons",
lodash: "lodash/lodash",
select2: "select2/dist/js/select2.full",
csrf: "../ui/js/csrf",
facets: "../ui/js/facets",
listing: "../ui/js/listing",
Expand Down
8 changes: 8 additions & 0 deletions ui/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,14 @@
rel="stylesheet"
href="{% static "bower/bootstrap/dist/css/bootstrap.css" %}"
>
<link
rel="stylesheet"
href="{% static "bower/select2/dist/css/select2.css" %}"
>
<link
rel="stylesheet"
href="{% static "bower/select2-bootstrap-theme/dist/select2-bootstrap.css" %}"
>
{# Site css #}
<link rel="stylesheet" href="{% static "ui/css/slide-drawer.css" %}">
<link rel="stylesheet" href="{% static "ui/css/mit-lore.css" %}">
Expand Down

0 comments on commit b4e3321

Please sign in to comment.