From 59c737aecffe1595ea1049e7a914b740a16c4526 Mon Sep 17 00:00:00 2001 From: Jamie Folsom Date: Fri, 7 Aug 2015 14:02:53 -0400 Subject: [PATCH] Changed how vocabulary terms are applied to Learning Resources to use two dropdowns instead of a growing list of fields. --- ui/jstests/test-learning-resource.jsx | 159 +++++++++++------- ui/static/ui/css/mit-lore.css | 30 +++- ui/static/ui/js/learning_resources.jsx | 215 ++++++++++++++++++++----- ui/static/ui/js/utils.jsx | 13 +- 4 files changed, 311 insertions(+), 106 deletions(-) diff --git a/ui/jstests/test-learning-resource.jsx b/ui/jstests/test-learning-resource.jsx index 13f23112..66d97801 100644 --- a/ui/jstests/test-learning-resource.jsx +++ b/ui/jstests/test-learning-resource.jsx @@ -3,7 +3,9 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', QUnit, $, React, _, LearningResources, TestUtils) { 'use strict'; - var VocabularyOption = LearningResources.VocabularyOption; + var VocabSelect = LearningResources.VocabSelect; + var TermList = LearningResources.TermList; + var TermSelect = LearningResources.TermSelect; var LearningResourcePanel = LearningResources.LearningResourcePanel; var waitForAjax = TestUtils.waitForAjax; @@ -35,13 +37,13 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', "label": "hard", "weight": 1 }; - var prereqTermsResponseRequired = { + var preReqTermsResponseReq = { "id": 3, "slug": "required", "label": "required", "weight": 1 }; - var prereqTermsResponseNotRequired = { + var preReqTermsResponseNotReq = { "id": 4, "slug": "notrequired", "label": "notrequired", @@ -55,11 +57,11 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', "vocabulary_type": "m", "required": false, "weight": 2147483647, - "terms": [prereqTermsResponseRequired, prereqTermsResponseNotRequired], + "terms": [preReqTermsResponseReq, preReqTermsResponseNotReq], "multi_terms": true }; var vocabularyResponseDifficulty = { - "id": 1, + "id": 2, "slug": "difficulty", "name": "difficulty", "description": "Difficulty", @@ -67,8 +69,21 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', "required": false, "weight": 2147483647, "terms": [termResponseEasy, termResponseHard], - "multi_terms": false + "multi_terms": true }; + var selectedVocabulary = vocabularyResponseDifficulty; + var vocabulariesAndTerms = [ + { + "terms": [preReqTermsResponseReq, preReqTermsResponseNotReq], + "selectedTerms": [preReqTermsResponseReq, preReqTermsResponseNotReq], + 'vocabulary': vocabularyResponsePrereq, + }, + { + "terms": [termResponseEasy, termResponseHard], + "selectedTerms": [termResponseEasy, termResponseHard], + 'vocabulary': vocabularyResponseDifficulty, + } + ]; var vocabulariesResponseFirst = { "count": 1, "next": "/api/v1/repositories/repo/vocabularies/?type_name=course&page=2", @@ -81,12 +96,6 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', "previous": "/api/v1/repositories/repo/vocabularies/?type_name=course", "results": [vocabularyResponsePrereq] }; - var difficultyTermsResponse = { - "count": 2, - "next": null, - "previous": null, - "results": [termResponseEasy, termResponseHard] - }; QUnit.module('Test learning resource panel', { beforeEach: function() { @@ -119,76 +128,117 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', }); QUnit.test( - 'Assert that VocabularyOption renders properly', + 'Assert that VocabSelect renders properly', function (assert) { var done = assert.async(); - var afterMount = function (component) { + + var afterMount = function(component) { var $node = $(React.findDOMNode(component)); - // one vocabulary var $vocabSelect = $node.find("select"); assert.equal($vocabSelect.size(), 1); - // two terms - var $termsSelect = $vocabSelect.find("option"); - 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(); + }; + + React.addons.TestUtils.renderIntoDocument( + + ); + } + ); + + QUnit.test( + 'Assert that TermSelect renders properly', + function (assert) { + var done = assert.async(); + + var afterMount = function(component) { + var $node = $(React.findDOMNode(component)); + + var $termSelect = $node.find("select"); + assert.equal($termSelect.size(), 1); done(); }; React.addons.TestUtils.renderIntoDocument( - + ); } ); + QUnit.test( + 'Assert that TermList renders properly', + function (assert) { + var done = assert.async(); + var afterMount = function(component) { + var $node = $(React.findDOMNode(component)); + var $termList = $node.find("ul"); + assert.equal($termList.size(), 1); + done(); + }; + + React.addons.TestUtils.renderIntoDocument( + + ); + } + ); QUnit.test( 'Assert that LearningResourcePanel changes state properly', function(assert) { var done = assert.async(); - var afterMount = function(component) { // wait for calls to populate form waitForAjax(3, function () { - // one vocabulary + // two menus: vocabulary and terms. var $node = $(React.findDOMNode(component)); - var $vocabSelect = $node.find("select"); - assert.equal($vocabSelect.size(), 2); - // two terms, first vocab - var $terms1Select = $($vocabSelect[0]).find("option"); - assert.equal($terms1Select.size(), 2); - assert.equal($terms1Select[0].selected, false); - assert.equal($terms1Select[0].selected, false); + var $allSelects = $node.find("#vocabularies select"); + assert.equal($allSelects.size(), 2); + + var $vocabSelect = $node.find($allSelects).first(); + assert.equal($vocabSelect.size(), 1); + + // first vocab, two options + var $vocabOptions = $vocabSelect.find("option"); + assert.equal($vocabOptions.size(), 2); + + assert.equal($vocabOptions[0].selected, true); + assert.equal($vocabOptions[1].selected, false); // TestUtils.Simulate.change only simulates a change event, // we need to update the value first ourselves - $($vocabSelect[0]).val("hard").trigger('change'); - React.addons.TestUtils.Simulate.change($vocabSelect[0]); + $vocabSelect.val("prerequisite").trigger('change'); + React.addons.TestUtils.Simulate.change($vocabSelect); component.forceUpdate(function() { - assert.equal($terms1Select[0].selected, false); - 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, true); - assert.equal($terms2Select[1].selected, false); - //the second vocabulary can be a multi select - $($vocabSelect[1]) - .val(["notrequired", "required"]) + assert.equal($vocabOptions[0].selected, false); + assert.equal($vocabOptions[1].selected, true); + // on selection of the second vocabulary, make sure the term selector updates. + var termsSelect = $allSelects[1]; + var $termsOptions = $(termsSelect).find("option"); + assert.equal($termsOptions.size(), 2); + assert.equal($termsOptions[0].selected, false); + assert.equal($termsOptions[1].selected, false); + // the second vocabulary can be a multi select + $(termsSelect) + .val(["hard", "easy"]) .trigger('change'); - React.addons.TestUtils.Simulate.change($vocabSelect[1]); + React.addons.TestUtils.Simulate.change(termsSelect); component.forceUpdate(function() { - assert.equal($terms2Select[0].selected, true); - assert.equal($terms2Select[1].selected, true); - //be sure that the state reflects the selection + assert.equal($termsOptions[0].selected, true); + assert.equal($termsOptions[1].selected, true); + // be sure that the state reflects the selection var terms = _.map(component.state.vocabulariesAndTerms, function (tuple) { return tuple.selectedTerms; @@ -196,13 +246,11 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', terms = _.flatten(terms); terms.sort(); var expectedTerms = [ - $terms1Select[1].value, - $terms2Select[0].value, - $terms2Select[1].value + $termsOptions[0].value, + $termsOptions[1].value ]; expectedTerms.sort(); assert.deepEqual(terms, expectedTerms); - done(); }); }); @@ -215,7 +263,6 @@ define(['QUnit', 'jquery', 'react', 'lodash', 'learning_resources', ref={afterMount} />); } ); - QUnit.test( 'Assert that LearningResourcePanel saves properly', function(assert) { diff --git a/ui/static/ui/css/mit-lore.css b/ui/static/ui/css/mit-lore.css index 22075e76..e3c47877 100644 --- a/ui/static/ui/css/mit-lore.css +++ b/ui/static/ui/css/mit-lore.css @@ -324,8 +324,8 @@ a { } .tile-meta { - font-size:small; - color:#929292; + font-size:small; + color:#929292; } .meta-item { @@ -497,6 +497,32 @@ ul.with-utility-features { word-wrap: break-word; } +div#term-list-container .panel-heading { + padding: 10px 15px; +} + +ul#term-list li.applied-term span.label { + color:black; +} + +ul#term-list { + list-style: none; + padding-left:0; +} + +div#term-list-container .panel-heading { + padding: 10px 15px; +} + +ul#term-list li.applied-term span.label { + color:black; +} + +ul#term-list { + list-style: none; + padding-left:0; +} + /* welcome/home page */ .section-header { diff --git a/ui/static/ui/js/learning_resources.jsx b/ui/static/ui/js/learning_resources.jsx index ae797666..795dcec0 100644 --- a/ui/static/ui/js/learning_resources.jsx +++ b/ui/static/ui/js/learning_resources.jsx @@ -6,73 +6,185 @@ define('learning_resources', [ var StatusBox = Utils.StatusBox; var Select2 = Utils.Select2; - var VocabularyOption = React.createClass({ + var TermList = React.createClass({ render: function () { - var options = _.map(this.props.terms, function (term) { + var appliedVocabularies = this.props.vocabs.map(function (vocab) { + var selectedTerms = vocab.selectedTerms; + var vocabularyName = vocab.vocabulary.name; + + if (selectedTerms.length) { + return ( + + ); + } + }); + return ( +
+
+ Taxonomy Terms applied to this Learning Resource +
+
    + {appliedVocabularies} +
+
+ ); + } + }); + + var TermListItem = React.createClass({ + render: function () { + return ( +
  • + {this.props.label}: {this.props.terms} +
  • + ); + } + }); + + var VocabSelect = React.createClass({ + render: function () { + var options; + options = _.map(this.props.vocabs, function(vocab) { + return { + id: vocab.vocabulary.slug, + text: vocab.vocabulary.name, + }; + }); + + var slug = this.props.selectedVocabulary.slug; + + return
    + +
    ; + }, + handleChange: function(e) { + var selectedValue = _.pluck( + _.filter(e.target.options, function(option) { + return option.selected && option.value !== null; + }), 'value'); + this.props.setValues( + _.pluck(this.props.vocabs, 'vocabulary'), selectedValue[0] + ); + } + }); + + var TermSelect = React.createClass({ + render: function () { + var options = []; + options = _.map(this.props.selectedVocabulary.terms, function (term) { return { id: term.slug, text: term.label }; }); - return
    - -
    + var currentVocabulary = {}; + var selectedVocabulary = this.props.selectedVocabulary; + + _.forEach(this.props.vocabs, function(vocab) { + if (vocab.vocabulary.slug === selectedVocabulary.slug) { + currentVocabulary = vocab; + return false; + } + }); + + var name = this.props.selectedVocabulary.name; + return
    -
    -
    ; +
    ; }, + handleChange: function(e) { - var selectValues = _.pluck( + var selectedValues = _.pluck( _.filter(e.target.options, function(option) { return option.selected && option.value !== null; }), 'value'); - this.props.updateTerms(this.props.vocabulary.slug, selectValues); + + this.props.setValues(this.props.selectedVocabulary.slug, selectedValues); } }); var LearningResourcePanel = React.createClass({ mixins: [React.addons.LinkedStateMixin], + + setSelectedVocabulary: function(vocabs, selectedValue) { + var selectedVocabulary = _.find( + vocabs, _.matchesProperty('slug', selectedValue) + ); + this.setState({ + selectedVocabulary: selectedVocabulary + }); + }, + + setSelectedTerms: function(vocabSlug, selectedTerms) { + var vocabulariesAndTerms = this.state.vocabulariesAndTerms; + var newVocabulariesAndTerms = _.map(vocabulariesAndTerms, + function(tuple) { + if (vocabSlug === tuple.vocabulary.slug) { + var newTuple = { + vocabulary: tuple.vocabulary, + terms: tuple.terms, + selectedTerms: selectedTerms + }; + return newTuple; + } else { + return tuple; + } + }); + + this.setState({ + vocabulariesAndTerms: newVocabulariesAndTerms + }); + }, + render: function () { - var thiz = this; + var vocabulariesAndTerms = this.state.vocabulariesAndTerms; - var options = _.map(vocabulariesAndTerms, function (pair) { - var updateTerms = function(vocabSlug, newTermsSlug) { - var newVocabulariesAndTerms = _.map(vocabulariesAndTerms, - function(tuple) { - if (vocabSlug === tuple.vocabulary.slug) { - return { - vocabulary: tuple.vocabulary, - terms: tuple.terms, - selectedTerms: newTermsSlug - }; - } else { - return tuple; - } - }); + var vocabSelector = "There are no terms for this resource"; + var termSelector = ""; + var termList = ""; - thiz.setState({ - vocabulariesAndTerms: newVocabulariesAndTerms - }); - }; + if (vocabulariesAndTerms.length) { + vocabSelector = + ; - return ; - }); + + termList = + ; + } return
    @@ -90,7 +202,12 @@ define('learning_resources', [ href={this.state.previewUrl} target="_blank">Preview

    - {options} +
    + {vocabSelector} {termSelector} +
    + + {termList} +