Skip to content
Snippets Groups Projects
Commit 74b81527 authored by Peter Fogg's avatar Peter Fogg
Browse files

Add a list view for metadata editor.

This is to be used with VideoAlpha's functionality allowing a choice
of multiple HTML5 video sources.
parent 4d5727a2
No related merge requests found
../../../templates/js/metadata-list-entry.underscore
\ No newline at end of file
......@@ -3,12 +3,14 @@ describe "Test Metadata Editor", ->
numberEntryTemplate = readFixtures('metadata-number-entry.underscore')
stringEntryTemplate = readFixtures('metadata-string-entry.underscore')
optionEntryTemplate = readFixtures('metadata-option-entry.underscore')
listEntryTemplate = readFixtures('metadata-list-entry.underscore')
beforeEach ->
setFixtures($("<script>", {id: "metadata-editor-tpl", type: "text/template"}).text(editorTemplate))
appendSetFixtures($("<script>", {id: "metadata-number-entry", type: "text/template"}).text(numberEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-string-entry", type: "text/template"}).text(stringEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-option-entry", type: "text/template"}).text(optionEntryTemplate))
appendSetFixtures($("<script>", {id: "metadata-list-entry", type: "text/template"}).text(listEntryTemplate))
genericEntry = {
default_value: 'default value',
......@@ -62,6 +64,18 @@ describe "Test Metadata Editor", ->
value: 10.2
}
listEntry = {
default_value: ["a thing", "another thing"],
display_name: "List",
explicitly_set: false,
field_name: "list",
help: "A list of things.",
inheritable: false,
options: [],
type: CMS.Models.Metadata.LIST_TYPE,
value: ["the first display value", "the second"]
}
# Test for the editor that creates the individual views.
describe "CMS.Views.Metadata.Editor creates editors for each field", ->
beforeEach ->
......@@ -84,16 +98,17 @@ describe "Test Metadata Editor", ->
{"display_name": "Never", "value": "never"}],
type: "unknown type",
value: null
}
},
listEntry
]
)
it "creates child views on initialize, and sorts them alphabetically", ->
view = new CMS.Views.Metadata.Editor({collection: @model})
childModels = view.collection.models
expect(childModels.length).toBe(5)
expect(childModels.length).toBe(6)
childViews = view.$el.find('.setting-input')
expect(childViews.length).toBe(5)
expect(childViews.length).toBe(6)
verifyEntry = (index, display_name, type) ->
expect(childModels[index].get('display_name')).toBe(display_name)
......@@ -101,9 +116,10 @@ describe "Test Metadata Editor", ->
verifyEntry(0, 'Display Name', 'text')
verifyEntry(1, 'Inputs', 'number')
verifyEntry(2, 'Show Answer', 'select-one')
verifyEntry(3, 'Unknown', 'text')
verifyEntry(4, 'Weight', 'number')
verifyEntry(2, 'List', 'text')
verifyEntry(3, 'Show Answer', 'select-one')
verifyEntry(4, 'Unknown', 'text')
verifyEntry(5, 'Weight', 'number')
it "returns its display name", ->
view = new CMS.Views.Metadata.Editor({collection: @model})
......@@ -146,27 +162,27 @@ describe "Test Metadata Editor", ->
# Tests for individual views.
assertInputType = (view, expectedType) ->
input = view.$el.find('.setting-input')
expect(input.length).toBe(1)
expect(input[0].type).toBe(expectedType)
expect(input.length).toEqual(1)
expect(input[0].type).toEqual(expectedType)
assertValueInView = (view, expectedValue) ->
expect(view.getValueFromEditor()).toBe(expectedValue)
expect(view.getValueFromEditor()).toEqual(expectedValue)
assertCanUpdateView = (view, newValue) ->
view.setValueInEditor(newValue)
expect(view.getValueFromEditor()).toBe(newValue)
expect(view.getValueFromEditor()).toEqual(newValue)
assertClear = (view, modelValue, editorValue=modelValue) ->
view.clear()
expect(view.model.getValue()).toBe(null)
expect(view.model.getDisplayValue()).toBe(modelValue)
expect(view.getValueFromEditor()).toBe(editorValue)
expect(view.model.getDisplayValue()).toEqual(modelValue)
expect(view.getValueFromEditor()).toEqual(editorValue)
assertUpdateModel = (view, originalValue, newValue) ->
view.setValueInEditor(newValue)
expect(view.model.getValue()).toBe(originalValue)
expect(view.model.getValue()).toEqual(originalValue)
view.updateModel()
expect(view.model.getValue()).toBe(newValue)
expect(view.model.getValue()).toEqual(newValue)
describe "CMS.Views.Metadata.String is a basic string input with clear functionality", ->
beforeEach ->
......@@ -298,3 +314,23 @@ describe "Test Metadata Editor", ->
verifyDisallowedChars(@integerView)
verifyDisallowedChars(@floatView)
describe "CMS.Views.Metadata.List allows the user to enter an ordered list of strings", ->
beforeEach ->
listModel = new CMS.Models.Metadata(listEntry)
@listView = new CMS.Views.Metadata.List({model: listModel})
it "uses a text input type", ->
assertInputType(@listView, 'text')
it "returns the initial value upon initialization", ->
assertValueInView(@listView, ['the first display value', 'the second'])
it "updates its value correctly", ->
assertCanUpdateView(@listView, ['a new item', 'another new item', 'a third'])
it "has a clear method to revert to the model default", ->
assertClear(@listView, ['a thing', 'another thing'])
it "has an update model method", ->
assertUpdateModel(@listView, null, ['a new value'])
......@@ -111,3 +111,4 @@ CMS.Models.Metadata.SELECT_TYPE = "Select";
CMS.Models.Metadata.INTEGER_TYPE = "Integer";
CMS.Models.Metadata.FLOAT_TYPE = "Float";
CMS.Models.Metadata.GENERIC_TYPE = "Generic";
CMS.Models.Metadata.LIST_TYPE = "List";
......@@ -27,6 +27,9 @@ CMS.Views.Metadata.Editor = Backbone.View.extend({
model.getType() === CMS.Models.Metadata.FLOAT_TYPE) {
new CMS.Views.Metadata.Number(data);
}
else if(model.getType() === CMS.Models.Metadata.LIST_TYPE) {
new CMS.Views.Metadata.List(data);
}
else {
// Everything else is treated as GENERIC_TYPE, which uses String editor.
new CMS.Views.Metadata.String(data);
......@@ -310,3 +313,23 @@ CMS.Views.Metadata.Option = CMS.Views.Metadata.AbstractEditor.extend({
}).prop('selected', true);
}
});
CMS.Views.Metadata.List = CMS.Views.Metadata.AbstractEditor.extend({
events : {
"click .setting-clear" : "clear",
"keypress .setting-input" : "showClearButton",
"change input" : "updateModel"
},
templateName: "metadata-list-entry",
getValueFromEditor: function () {
return _.map(this.$el.find('#' + this.uniqueId).val().split(/,/),
function (url) { return url.trim(); });
},
setValueInEditor: function (value) {
this.$el.find('.input').val(value.join(', '));
}
});
<div class="wrapper-comp-setting">
<label class="label setting-label" for="<%= uniqueId %>"><%= model.get('display_name')%></label>
<input class="input setting-input" type="text" id="<%= uniqueId %>" value='<%= model.get("value") %>'/>
<button class="action setting-clear inactive" type="button" name="setting-clear" value="<%= gettext("Clear") %>" data-tooltip="<%= gettext("Clear") %>">
<i class="icon-undo"></i><span class="sr">"<%= gettext("Clear Value") %>"</span>
</button>
</div>
<span class="tip setting-help"><%= model.get('help') %></span>
......@@ -25,6 +25,10 @@
<%static:include path="js/metadata-option-entry.underscore" />
</script>
<script id="metadata-list-entry" type="text/template">
<%static:include path="js/metadata-list-entry.underscore" />
</script>
<% showHighLevelSource='source_code' in editable_metadata_fields and editable_metadata_fields['source_code']['explicitly_set'] %>
<% metadata_field_copy = copy.copy(editable_metadata_fields) %>
## Delete 'source_code' field (if it exists) so metadata editor view does not attempt to render it.
......@@ -40,4 +44,4 @@
<%include file="source-edit.html" />
% endif
<div class="wrapper-comp-settings metadata_edit" id="settings-tab" data-metadata='${json.dumps(metadata_field_copy) | h}'/>
\ No newline at end of file
<div class="wrapper-comp-settings metadata_edit" id="settings-tab" data-metadata='${json.dumps(metadata_field_copy) | h}'/>
......@@ -10,7 +10,7 @@ from pkg_resources import resource_listdir, resource_string, resource_isdir
from xmodule.modulestore import inheritance, Location
from xmodule.modulestore.exceptions import ItemNotFoundError, InsufficientSpecificationError, InvalidLocationError
from xblock.core import XBlock, Scope, String, Integer, Float, ModelType
from xblock.core import XBlock, Scope, String, Integer, Float, List, ModelType
from xblock.fragment import Fragment
from xblock.runtime import Runtime
from xmodule.modulestore.locator import BlockUsageLocator
......@@ -766,7 +766,7 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
# 2. Number editors for integers and floats.
# 3. A generic string editor for anything else (editing JSON representation of the value).
editor_type = "Generic"
values = [] if field.values is None else copy.deepcopy(field.values)
values = copy.deepcopy(field.values)
if isinstance(values, tuple):
values = list(values)
if isinstance(values, list):
......@@ -783,11 +783,13 @@ class XModuleDescriptor(XModuleFields, HTMLSnippet, ResourceTemplates, XBlock):
editor_type = "Integer"
elif isinstance(field, Float):
editor_type = "Float"
elif isinstance(field, List):
editor_type = "List"
metadata_fields[field.name] = {'field_name': field.name,
'type': editor_type,
'display_name': field.display_name,
'value': field.to_json(value),
'options': values,
'options': [] if values is None else values,
'default_value': field.to_json(default_value),
'inheritable': inheritable,
'explicitly_set': explicitly_set,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment