diff --git a/ui/jstests/test-learning-resource.jsx b/ui/jstests/test-learning-resource.jsx index 888c1ee1..e43bf956 100644 --- a/ui/jstests/test-learning-resource.jsx +++ b/ui/jstests/test-learning-resource.jsx @@ -123,13 +123,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(); }; @@ -158,10 +156,9 @@ 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 @@ -169,14 +166,12 @@ define(['QUnit', 'jquery', 'learning_resources', 'reactaddons', 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); + assert.equal($terms2Select.size(), 1); + assert.equal($terms2Select[0].selected, true); done(); }); diff --git a/ui/static/ui/js/learning_resources.jsx b/ui/static/ui/js/learning_resources.jsx index 73b6aa7c..25b2003e 100644 --- a/ui/static/ui/js/learning_resources.jsx +++ b/ui/static/ui/js/learning_resources.jsx @@ -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 ; + 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
-
; }, - 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'); } }); @@ -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; @@ -60,9 +89,9 @@ define('learning_resources', [ return ; }); @@ -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 @@ -159,7 +185,6 @@ define('learning_resources', [ description: description, previewUrl: previewUrl, }); - Utils.getVocabulariesAndTerms( thiz.props.repoSlug, learningResourceType) .then(function (results) { @@ -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 }; }); diff --git a/ui/static/ui/js/require_config.js b/ui/static/ui/js/require_config.js index 374084da..819a70f4 100644 --- a/ui/static/ui/js/require_config.js +++ b/ui/static/ui/js/require_config.js @@ -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", diff --git a/ui/templates/base.html b/ui/templates/base.html index b7b09f4f..9d3d6b44 100644 --- a/ui/templates/base.html +++ b/ui/templates/base.html @@ -29,6 +29,14 @@ rel="stylesheet" href="{% static "bower/bootstrap/dist/css/bootstrap.css" %}" > + + {# Site css #}