Add tags plugin.

TNL-1924
This commit is contained in:
cahrens
2015-04-27 22:07:30 -04:00
parent ced607d5e4
commit 5fe384ca2b
10 changed files with 229 additions and 30 deletions

View File

@@ -131,19 +131,27 @@ define(['jquery', 'underscore', 'annotator_1.2.9'], function ($, _, Annotator) {
},
getEditorTabControls: function () {
var editor, editorControls, textArea, saveButton, cancelButton, tabControls = [];
var editor, editorControls, textArea, saveButton, cancelButton, tabControls = [], annotatorItems,
tagInput = null;
// Editor elements
editor = this.annotator.element.find('.annotator-editor');
editorControls = editor.find('.annotator-controls');
textArea = editor.find('.annotator-listing')
.find('.annotator-item')
.first()
.children('textarea');
annotatorItems = editor.find('.annotator-listing').find('.annotator-item');
textArea = annotatorItems.first().children('textarea');
saveButton = editorControls.find('.annotator-save');
cancelButton = editorControls.find('.annotator-cancel');
tabControls.push(textArea, saveButton, cancelButton);
// If the tags plugin is enabled, add the ability to tab into it.
if (annotatorItems.length > 1) {
tagInput = annotatorItems.first().next().children('input');
}
tabControls.push(textArea);
if (tagInput){
tabControls.push(tagInput);
}
tabControls.push(saveButton, cancelButton);
return tabControls;
},

View File

@@ -6,7 +6,7 @@ define([
'js/edxnotes/plugins/events', 'js/edxnotes/plugins/accessibility',
'js/edxnotes/plugins/caret_navigation'
], function ($, _, Annotator, NotesLogger) {
var plugins = ['Auth', 'Store', 'Scroller', 'Events', 'Accessibility', 'CaretNavigation'],
var plugins = ['Auth', 'Store', 'Scroller', 'Events', 'Accessibility', 'CaretNavigation', 'Tags'],
getOptions, setupPlugins, updateHeaders, getAnnotator;
/**

View File

@@ -58,6 +58,34 @@ define([
return (timeToExpiry > 0) ? timeToExpiry : 0;
};
Annotator.Plugin.Tags.prototype.updateField = _.compose(
function() {
// Add screen reader label for edit mode. Note that the id of the tags element will not always be "1".
// It depends on the number of annotatable components on the page.
var tagsField = $("li.annotator-item >input", this.annotator.editor.element).attr('id');
if ($("label.sr[for='"+ tagsField + "']", this.annotator.editor.element).length === 0) {
$('<label class="sr" for='+ tagsField +'>' + _t('Tags (space-separated)') + '</label>').insertBefore(
$('#'+tagsField, this.annotator.editor.element)
);
}
return this;
},
Annotator.Plugin.Tags.prototype.updateField
);
Annotator.Plugin.Tags.prototype.updateViewer = _.compose(
function() {
// Add ARIA information for viewing mode.
$('div.annotator-tags', this.wrapper).attr({
'role': 'region',
'aria-label': 'tags'
});
return this;
},
Annotator.Plugin.Tags.prototype.updateViewer
);
/**
* Modifies Annotator.highlightRange to add "tabindex=0" and role="link"
* attributes to the <span class="annotator-hl"> markup that encloses the
@@ -186,25 +214,24 @@ define([
'</div>'
].join('');
/**
* Modifies Annotator._setupEditor to add a label for textarea#annotator-field-0.
**/
Annotator.prototype._setupEditor = _.compose(
function () {
$('<label class="sr" for="annotator-field-0">Edit note</label>').insertBefore(
$('#annotator-field-0', this.wrapper)
);
return this;
},
Annotator.prototype._setupEditor
);
/**
* Modifies Annotator.Editor.show, in the case of a keydown event, to remove
* focus from Save button and put it on form.annotator-widget instead.
*
* Also add a sr label for note textarea.
**/
Annotator.Editor.prototype.show = _.compose(
function (event) {
// Add screen reader label for the note area. Note that the id of the tags element will not always be "0".
// It depends on the number of annotatable components on the page.
var noteField = $("li.annotator-item >textarea", this.element).attr('id');
if ($("label.sr[for='"+ noteField + "']", this.element).length === 0) {
$('<label class="sr" for='+ noteField +'>' + _t('Note') + '</label>').insertBefore(
$('#'+noteField, this.element)
);
}
if (event.type === 'keydown') {
this.element.find('.annotator-save').removeClass(this.classes.focus);
this.element.find('form.annotator-widget').focus();

View File

@@ -168,12 +168,12 @@ define([
};
highlight.data('annotation', annotation);
this.annotator.viewer.load([annotation]);
listing = this.annotator.element.find('.annotator-listing').first(),
listing = this.annotator.element.find('.annotator-listing').first();
note = this.annotator.element.find('.annotator-note').first();
edit= this.annotator.element.find('.annotator-edit').first();
del = this.annotator.element.find('.annotator-delete').first();
close = this.annotator.element.find('.annotator-close').first();
spyOn(this.annotator.viewer, 'hide').andCallThrough();;
spyOn(this.annotator.viewer, 'hide').andCallThrough();
});
it('should give focus to Note on Listing TAB keydown', function () {
@@ -224,7 +224,7 @@ define([
});
describe('keydown events on editor', function () {
var highlight, annotation, form, textArea, save, cancel;
var highlight, annotation, form, annotatorItems, textArea, tags, save, cancel;
beforeEach(function() {
highlight = $('<span class="annotator-hl" tabindex="0"/>').appendTo(this.annotator.element);
@@ -236,7 +236,9 @@ define([
highlight.data('annotation', annotation);
this.annotator.editor.show(annotation, {'left': 0, 'top': 0});
form = this.annotator.element.find('form.annotator-widget');
textArea = this.annotator.element.find('.annotator-item').first().children('textarea');
annotatorItems = this.annotator.element.find('.annotator-item');
textArea = annotatorItems.first().children('textarea');
tags = annotatorItems.first().next().children('input');
save = this.annotator.element.find('.annotator-save');
cancel = this.annotator.element.find('.annotator-cancel');
spyOn(this.annotator.editor, 'submit').andCallThrough();
@@ -255,9 +257,11 @@ define([
expect(cancel).toBeFocused();
});
it('should cycle forward through texarea, save, and cancel on TAB keydown', function () {
it('should cycle forward through textarea, tags, save, and cancel on TAB keydown', function () {
textArea.focus();
textArea.trigger(tabForwardEvent());
expect(tags).toBeFocused();
tags.trigger(tabForwardEvent());
expect(save).toBeFocused();
save.trigger(tabForwardEvent());
expect(cancel).toBeFocused();
@@ -265,13 +269,15 @@ define([
expect(textArea).toBeFocused();
});
it('should cycle back through texarea, save, and cancel on SHIFT + TAB keydown', function () {
it('should cycle back through textarea, tags, save, and cancel on SHIFT + TAB keydown', function () {
textArea.focus();
textArea.trigger(tabBackwardEvent());
expect(cancel).toBeFocused();
cancel.trigger(tabBackwardEvent());
expect(save).toBeFocused();
save.trigger(tabBackwardEvent());
expect(tags).toBeFocused();
tags.trigger(tabBackwardEvent());
expect(textArea).toBeFocused();
});

View File

@@ -233,5 +233,37 @@ define([
expect(mockViewer.element.appendTo).toHaveBeenCalledWith(annotators[0].wrapper);
});
});
describe('TagsPlugin', function () {
it('should add ARIA label information to the viewer', function() {
var tagDiv,
annotation = {
id: '01',
text: "Test text",
tags: ["tag1", "tag2", "tag3"],
highlights: [highlights[0].get(0)]
};
annotators[0].viewer.load([annotation]);
tagDiv = annotators[0].viewer.element.find('.annotator-tags');
expect($(tagDiv).attr('role')).toEqual('region');
expect($(tagDiv).attr('aria-label')).toEqual('tags');
// Three children for the individual tags.
expect($(tagDiv).children().length).toEqual(3);
});
it('should add screen reader label to the editor', function() {
var srLabel, editor, inputId;
// We don't know exactly what the input ID will be (depends on number of annotatable components shown),
// but the sr label "for" attribute should match the ID of the element immediately following it.
annotators[0].showEditor({}, {});
editor = annotators[0].editor;
srLabel = editor.element.find("label.sr");
inputId = srLabel.next().attr('id');
expect(srLabel.attr('for')).toEqual(inputId);
});
});
});
});

View File

@@ -212,7 +212,7 @@ $divider-visual-tertiary: ($baseline/20) solid $gray-l4;
.reference-title {
@extend %t-title8;
@extend %t-weight3;
margin-top: $baseline;
margin-top: ($baseline/2);
text-transform: uppercase;
letter-spacing: ($baseline/20);
color: $gray-l2;